diff --git a/.coveragerc b/.coveragerc index 723242b288..12e48ec395 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,2 +1,4 @@ [run] -omit = esphome/components/* +omit = + esphome/components/* + tests/integration/* diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000..51e2232d24 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,37 @@ +ARG BUILD_BASE_VERSION=2025.04.0 + + +FROM ghcr.io/esphome/docker-base:debian-${BUILD_BASE_VERSION} AS base + +RUN git config --system --add safe.directory "*" + +RUN apt update \ + && apt install -y \ + protobuf-compiler + +RUN pip install uv + +RUN useradd esphome -m + +USER esphome +ENV VIRTUAL_ENV=/home/esphome/.local/esphome-venv +RUN uv venv $VIRTUAL_ENV +ENV PATH="$VIRTUAL_ENV/bin:$PATH" +# Override this set to true in the docker-base image +ENV UV_SYSTEM_PYTHON=false + +WORKDIR /tmp + +COPY requirements.txt ./ +RUN uv pip install -r requirements.txt +COPY requirements_dev.txt requirements_test.txt ./ +RUN uv pip install -r requirements_dev.txt -r requirements_test.txt + +RUN \ + platformio settings set enable_telemetry No \ + && platformio settings set check_platformio_interval 1000000 + +COPY script/platformio_install_deps.py platformio.ini ./ +RUN ./platformio_install_deps.py platformio.ini --libraries --platforms --tools + +WORKDIR /workspaces diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 86f35cc47b..5a7a02a266 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,18 +1,17 @@ { "name": "ESPHome Dev", - "image": "ghcr.io/esphome/esphome-lint:dev", + "context": "..", + "dockerFile": "Dockerfile", "postCreateCommand": [ "script/devcontainer-post-create" ], - "containerEnv": { - "DEVCONTAINER": "1", - "PIP_BREAK_SYSTEM_PACKAGES": "1", - "PIP_ROOT_USER_ACTION": "ignore" + "features": { + "ghcr.io/devcontainers/features/github-cli:1": {} }, "runArgs": [ "--privileged", "-e", - "ESPHOME_DASHBOARD_USE_PING=1" + "GIT_EDITOR=code --wait" // uncomment and edit the path in order to pass though local USB serial to the conatiner // , "--device=/dev/ttyACM0" ], diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index 0e41314ce6..403b9d8c2a 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -47,7 +47,7 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr - uses: docker/build-push-action@v6.17.0 + uses: docker/build-push-action@v6.18.0 env: DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_RECORD_UPLOAD: false @@ -73,7 +73,7 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub - uses: docker/build-push-action@v6.17.0 + uses: docker/build-push-action@v6.18.0 env: DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_RECORD_UPLOAD: false diff --git a/.github/workflows/ci-api-proto.yml b/.github/workflows/ci-api-proto.yml index 92d209cc34..f51bd84186 100644 --- a/.github/workflows/ci-api-proto.yml +++ b/.github/workflows/ci-api-proto.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Set up Python uses: actions/setup-python@v5.6.0 with: diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 511ec55f3e..5f524612ed 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -43,11 +43,11 @@ jobs: - "docker" # - "lint" steps: - - uses: actions/checkout@v4.1.7 + - uses: actions/checkout@v4.2.2 - name: Set up Python uses: actions/setup-python@v5.6.0 with: - python-version: "3.9" + python-version: "3.10" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3.10.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d2ec68010..ca6d1b0aac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,8 +20,8 @@ permissions: contents: read env: - DEFAULT_PYTHON: "3.9" - PYUPGRADE_TARGET: "--py39-plus" + DEFAULT_PYTHON: "3.10" + PYUPGRADE_TARGET: "--py310-plus" concurrency: # yamllint disable-line rule:line-length @@ -36,7 +36,7 @@ jobs: cache-key: ${{ steps.cache-key.outputs.key }} steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Generate cache-key id: cache-key run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT @@ -68,7 +68,7 @@ jobs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -89,7 +89,7 @@ jobs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -110,7 +110,7 @@ jobs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -131,7 +131,7 @@ jobs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -152,7 +152,7 @@ jobs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -173,10 +173,10 @@ jobs: fail-fast: false matrix: python-version: - - "3.9" - "3.10" - "3.11" - "3.12" + - "3.13" os: - ubuntu-latest - macOS-latest @@ -185,24 +185,24 @@ jobs: # Minimize CI resource usage # by only running the Python version # version used for docker images on Windows and macOS + - python-version: "3.13" + os: windows-latest - python-version: "3.12" os: windows-latest - python-version: "3.10" os: windows-latest - - python-version: "3.9" - os: windows-latest + - python-version: "3.13" + os: macOS-latest - python-version: "3.12" os: macOS-latest - python-version: "3.10" os: macOS-latest - - python-version: "3.9" - os: macOS-latest runs-on: ${{ matrix.os }} needs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -214,14 +214,14 @@ jobs: if: matrix.os == 'windows-latest' run: | ./venv/Scripts/activate - pytest -vv --cov-report=xml --tb=native tests + pytest -vv --cov-report=xml --tb=native -n auto tests - name: Run pytest if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest' run: | . venv/bin/activate - pytest -vv --cov-report=xml --tb=native tests + pytest -vv --cov-report=xml --tb=native -n auto tests - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5.4.2 + uses: codecov/codecov-action@v5.4.3 with: token: ${{ secrets.CODECOV_TOKEN }} @@ -232,7 +232,7 @@ jobs: - common steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -296,11 +296,11 @@ jobs: name: Run script/clang-tidy for ZEPHYR options: --environment nrf52-tidy --grep USE_ZEPHYR pio_cache_key: tidy-zephyr - ignore_errors: true + ignore_errors: false steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -356,7 +356,7 @@ jobs: count: ${{ steps.list-components.outputs.count }} steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 with: # Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works. fetch-depth: 500 @@ -406,7 +406,7 @@ jobs: sudo apt-get install libsdl2-dev - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -432,7 +432,7 @@ jobs: matrix: ${{ steps.split.outputs.components }} steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Split components into 20 groups id: split run: | @@ -462,7 +462,7 @@ jobs: sudo apt-get install libsdl2-dev - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Restore Python uses: ./.github/actions/restore-python with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8569443881..eae01fe0b3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: branch_build: ${{ steps.tag.outputs.branch_build }} deploy_env: ${{ steps.tag.outputs.deploy_env }} steps: - - uses: actions/checkout@v4.1.7 + - uses: actions/checkout@v4.2.2 - name: Get tag id: tag # yamllint disable rule:line-length @@ -60,7 +60,7 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@v4.1.7 + - uses: actions/checkout@v4.2.2 - name: Set up Python uses: actions/setup-python@v5.6.0 with: @@ -92,11 +92,11 @@ jobs: os: "ubuntu-24.04-arm" steps: - - uses: actions/checkout@v4.1.7 + - uses: actions/checkout@v4.2.2 - name: Set up Python uses: actions/setup-python@v5.6.0 with: - python-version: "3.9" + python-version: "3.10" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3.10.0 @@ -168,7 +168,7 @@ jobs: - ghcr - dockerhub steps: - - uses: actions/checkout@v4.1.7 + - uses: actions/checkout@v4.2.2 - name: Download digests uses: actions/download-artifact@v4.3.0 diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index b262a9f9c1..a38825fc45 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -13,10 +13,10 @@ jobs: if: github.repository == 'esphome/esphome' steps: - name: Checkout - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Checkout Home Assistant - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 with: repository: home-assistant/core path: lib/home-assistant @@ -24,7 +24,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5.6.0 with: - python-version: 3.12 + python-version: 3.13 - name: Install Home Assistant run: | diff --git a/.github/workflows/yaml-lint.yml b/.github/workflows/yaml-lint.yml index 1c0b5f58ad..ed9b4407a2 100644 --- a/.github/workflows/yaml-lint.yml +++ b/.github/workflows/yaml-lint.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.2 - name: Run yamllint uses: frenck/action-yamllint@v1.5.0 with: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c3d5b9c783..d55c00eea7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.11.9 + rev: v0.11.10 hooks: # Run the linter. - id: ruff @@ -28,10 +28,10 @@ repos: - --branch=release - --branch=beta - repo: https://github.com/asottile/pyupgrade - rev: v3.15.2 + rev: v3.20.0 hooks: - id: pyupgrade - args: [--py39-plus] + args: [--py310-plus] - repo: https://github.com/adrienverge/yamllint.git rev: v1.37.1 hooks: diff --git a/CODEOWNERS b/CODEOWNERS index a6e08f225d..66ea80f8d6 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -96,6 +96,7 @@ esphome/components/ch422g/* @clydebarrow @jesterret esphome/components/chsc6x/* @kkosik20 esphome/components/climate/* @esphome/core esphome/components/climate_ir/* @glmnet +esphome/components/cm1106/* @andrewjswan esphome/components/color_temperature/* @jesserockz esphome/components/combination/* @Cat-Ion @kahrendt esphome/components/const/* @esphome/core @@ -138,6 +139,7 @@ esphome/components/es7210/* @kahrendt esphome/components/es7243e/* @kbx81 esphome/components/es8156/* @kbx81 esphome/components/es8311/* @kahrendt @kroimon +esphome/components/es8388/* @P4uLT esphome/components/esp32/* @esphome/core esphome/components/esp32_ble/* @Rapsssito @jesserockz esphome/components/esp32_ble_client/* @jesserockz @@ -148,6 +150,7 @@ esphome/components/esp32_improv/* @jesserockz esphome/components/esp32_rmt/* @jesserockz esphome/components/esp32_rmt_led_strip/* @jesserockz esphome/components/esp8266/* @esphome/core +esphome/components/esp_ldo/* @clydebarrow esphome/components/ethernet_info/* @gtjadsonsantos esphome/components/event/* @nohat esphome/components/event_emitter/* @Rapsssito @@ -233,6 +236,7 @@ esphome/components/kamstrup_kmp/* @cfeenstra1024 esphome/components/key_collector/* @ssieb esphome/components/key_provider/* @ssieb esphome/components/kuntze/* @ssieb +esphome/components/lc709203f/* @ilikecake esphome/components/lcd_menu/* @numo68 esphome/components/ld2410/* @regevbr @sebcaps esphome/components/ld2420/* @descipher @@ -318,6 +322,7 @@ esphome/components/number/* @esphome/core esphome/components/one_wire/* @ssieb esphome/components/online_image/* @clydebarrow @guillempages esphome/components/opentherm/* @olegtarasov +esphome/components/openthread/* @mrene esphome/components/ota/* @esphome/core esphome/components/output/* @esphome/core esphome/components/packet_transport/* @clydebarrow @@ -478,6 +483,8 @@ esphome/components/ufire_ise/* @pvizeli esphome/components/ultrasonic/* @OttoWinter esphome/components/update/* @jesserockz esphome/components/uponor_smatrix/* @kroimon +esphome/components/usb_host/* @clydebarrow +esphome/components/usb_uart/* @clydebarrow esphome/components/valve/* @esphome/core esphome/components/vbus/* @ssieb esphome/components/veml3235/* @kbx81 diff --git a/Doxyfile b/Doxyfile index c2bde26ded..d1e950c548 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.5.2 +PROJECT_NUMBER = 2025.6.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/__main__.py b/esphome/__main__.py index 9f638456e6..2dbdfeb1ff 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -134,6 +134,7 @@ def get_port_type(port): def run_miniterm(config, port, args): + from aioesphomeapi import LogParser import serial from esphome import platformio_api @@ -158,6 +159,7 @@ def run_miniterm(config, port, args): ser.dtr = False ser.rts = False + parser = LogParser() tries = 0 while tries < 5: try: @@ -174,8 +176,7 @@ def run_miniterm(config, port, args): .decode("utf8", "backslashreplace") ) time_str = datetime.now().time().strftime("[%H:%M:%S]") - message = time_str + line - safe_print(message) + safe_print(parser.parse_line(line, time_str)) backtrace_state = platformio_api.process_stacktrace( config, line, backtrace_state=backtrace_state @@ -593,15 +594,20 @@ def command_update_all(args): middle_text = f" {middle_text} " width = len(click.unstyle(middle_text)) half_line = "=" * ((twidth - width) // 2) - click.echo(f"{half_line}{middle_text}{half_line}") + safe_print(f"{half_line}{middle_text}{half_line}") for f in files: - print(f"Updating {color(AnsiFore.CYAN, f)}") - print("-" * twidth) - print() - rc = run_external_process( - "esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA" - ) + safe_print(f"Updating {color(AnsiFore.CYAN, f)}") + safe_print("-" * twidth) + safe_print() + if CORE.dashboard: + rc = run_external_process( + "esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA" + ) + else: + rc = run_external_process( + "esphome", "run", f, "--no-logs", "--device", "OTA" + ) if rc == 0: print_bar(f"[{color(AnsiFore.BOLD_GREEN, 'SUCCESS')}] {f}") success[f] = True @@ -609,17 +615,17 @@ def command_update_all(args): print_bar(f"[{color(AnsiFore.BOLD_RED, 'ERROR')}] {f}") success[f] = False - print() - print() - print() + safe_print() + safe_print() + safe_print() print_bar(f"[{color(AnsiFore.BOLD_WHITE, 'SUMMARY')}]") failed = 0 for f in files: if success[f]: - print(f" - {f}: {color(AnsiFore.GREEN, 'SUCCESS')}") + safe_print(f" - {f}: {color(AnsiFore.GREEN, 'SUCCESS')}") else: - print(f" - {f}: {color(AnsiFore.BOLD_RED, 'FAILED')}") + safe_print(f" - {f}: {color(AnsiFore.BOLD_RED, 'FAILED')}") failed += 1 return failed diff --git a/esphome/components/a4988/a4988.cpp b/esphome/components/a4988/a4988.cpp index ae2df3233a..72b3835cfd 100644 --- a/esphome/components/a4988/a4988.cpp +++ b/esphome/components/a4988/a4988.cpp @@ -7,7 +7,7 @@ namespace a4988 { static const char *const TAG = "a4988.stepper"; void A4988::setup() { - ESP_LOGCONFIG(TAG, "Setting up A4988..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (this->sleep_pin_ != nullptr) { this->sleep_pin_->setup(); this->sleep_pin_->digital_write(false); diff --git a/esphome/components/absolute_humidity/absolute_humidity.cpp b/esphome/components/absolute_humidity/absolute_humidity.cpp index 13f30c996f..c3cb159aed 100644 --- a/esphome/components/absolute_humidity/absolute_humidity.cpp +++ b/esphome/components/absolute_humidity/absolute_humidity.cpp @@ -7,7 +7,7 @@ namespace absolute_humidity { static const char *const TAG = "absolute_humidity.sensor"; void AbsoluteHumidityComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up absolute humidity '%s'...", this->get_name().c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str()); ESP_LOGD(TAG, " Added callback for temperature '%s'", this->temperature_sensor_->get_name().c_str()); this->temperature_sensor_->add_on_state_callback([this](float state) { this->temperature_callback_(state); }); @@ -40,9 +40,11 @@ void AbsoluteHumidityComponent::dump_config() { break; } - ESP_LOGCONFIG(TAG, "Sources"); - ESP_LOGCONFIG(TAG, " Temperature: '%s'", this->temperature_sensor_->get_name().c_str()); - ESP_LOGCONFIG(TAG, " Relative Humidity: '%s'", this->humidity_sensor_->get_name().c_str()); + ESP_LOGCONFIG(TAG, + "Sources\n" + " Temperature: '%s'\n" + " Relative Humidity: '%s'", + this->temperature_sensor_->get_name().c_str(), this->humidity_sensor_->get_name().c_str()); } float AbsoluteHumidityComponent::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/ac_dimmer/ac_dimmer.cpp b/esphome/components/ac_dimmer/ac_dimmer.cpp index 4901719b32..ddaa910db3 100644 --- a/esphome/components/ac_dimmer/ac_dimmer.cpp +++ b/esphome/components/ac_dimmer/ac_dimmer.cpp @@ -214,8 +214,10 @@ void AcDimmer::dump_config() { ESP_LOGCONFIG(TAG, "AcDimmer:"); LOG_PIN(" Output Pin: ", this->gate_pin_); LOG_PIN(" Zero-Cross Pin: ", this->zero_cross_pin_); - ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->store_.min_power / 10.0f); - ESP_LOGCONFIG(TAG, " Init with half cycle: %s", YESNO(this->init_with_half_cycle_)); + ESP_LOGCONFIG(TAG, + " Min Power: %.1f%%\n" + " Init with half cycle: %s", + this->store_.min_power / 10.0f, YESNO(this->init_with_half_cycle_)); if (method_ == DIM_METHOD_LEADING_PULSE) { ESP_LOGCONFIG(TAG, " Method: leading pulse"); } else if (method_ == DIM_METHOD_LEADING) { diff --git a/esphome/components/adc/adc_sensor_esp32.cpp b/esphome/components/adc/adc_sensor_esp32.cpp index 0f1d802937..d6cf6e893b 100644 --- a/esphome/components/adc/adc_sensor_esp32.cpp +++ b/esphome/components/adc/adc_sensor_esp32.cpp @@ -22,7 +22,7 @@ static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; void ADCSensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str()); if (this->channel1_ != ADC1_CHANNEL_MAX) { adc1_config_width(ADC_WIDTH_MAX_SOC_BITS); @@ -77,8 +77,10 @@ void ADCSensor::dump_config() { break; } } - ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); - ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); + ESP_LOGCONFIG(TAG, + " Samples: %i\n" + " Sampling mode: %s", + this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/adc/adc_sensor_esp8266.cpp b/esphome/components/adc/adc_sensor_esp8266.cpp index 9a12009abc..67128fb1f3 100644 --- a/esphome/components/adc/adc_sensor_esp8266.cpp +++ b/esphome/components/adc/adc_sensor_esp8266.cpp @@ -17,7 +17,7 @@ namespace adc { static const char *const TAG = "adc.esp8266"; void ADCSensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str()); #ifndef USE_ADC_SENSOR_VCC this->pin_->setup(); #endif @@ -30,8 +30,10 @@ void ADCSensor::dump_config() { #else LOG_PIN(" Pin: ", this->pin_); #endif // USE_ADC_SENSOR_VCC - ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); - ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); + ESP_LOGCONFIG(TAG, + " Samples: %i\n" + " Sampling mode: %s", + this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/adc/adc_sensor_libretiny.cpp b/esphome/components/adc/adc_sensor_libretiny.cpp index 9e75ed414c..f7c7e669ec 100644 --- a/esphome/components/adc/adc_sensor_libretiny.cpp +++ b/esphome/components/adc/adc_sensor_libretiny.cpp @@ -9,7 +9,7 @@ namespace adc { static const char *const TAG = "adc.libretiny"; void ADCSensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str()); #ifndef USE_ADC_SENSOR_VCC this->pin_->setup(); #endif // !USE_ADC_SENSOR_VCC @@ -22,8 +22,10 @@ void ADCSensor::dump_config() { #else // USE_ADC_SENSOR_VCC LOG_PIN(" Pin: ", this->pin_); #endif // USE_ADC_SENSOR_VCC - ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); - ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); + ESP_LOGCONFIG(TAG, + " Samples: %i\n" + " Sampling mode: %s", + this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/adc/adc_sensor_rp2040.cpp b/esphome/components/adc/adc_sensor_rp2040.cpp index f6cf1bac7a..91d331270b 100644 --- a/esphome/components/adc/adc_sensor_rp2040.cpp +++ b/esphome/components/adc/adc_sensor_rp2040.cpp @@ -14,7 +14,7 @@ namespace adc { static const char *const TAG = "adc.rp2040"; void ADCSensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str()); static bool initialized = false; if (!initialized) { adc_init(); @@ -33,8 +33,10 @@ void ADCSensor::dump_config() { LOG_PIN(" Pin: ", this->pin_); #endif // USE_ADC_SENSOR_VCC } - ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); - ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); + ESP_LOGCONFIG(TAG, + " Samples: %i\n" + " Sampling mode: %s", + this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/adc128s102/adc128s102.cpp b/esphome/components/adc128s102/adc128s102.cpp index c27a354dd3..c8e8edb359 100644 --- a/esphome/components/adc128s102/adc128s102.cpp +++ b/esphome/components/adc128s102/adc128s102.cpp @@ -9,7 +9,7 @@ static const char *const TAG = "adc128s102"; float ADC128S102::get_setup_priority() const { return setup_priority::HARDWARE; } void ADC128S102::setup() { - ESP_LOGCONFIG(TAG, "Setting up adc128s102"); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); } diff --git a/esphome/components/ade7880/ade7880.cpp b/esphome/components/ade7880/ade7880.cpp index 4a45b3b321..55f834bf86 100644 --- a/esphome/components/ade7880/ade7880.cpp +++ b/esphome/components/ade7880/ade7880.cpp @@ -177,11 +177,14 @@ void ADE7880::dump_config() { LOG_SENSOR(" ", "Power Factor", this->channel_a_->power_factor); LOG_SENSOR(" ", "Forward Active Energy", this->channel_a_->forward_active_energy); LOG_SENSOR(" ", "Reverse Active Energy", this->channel_a_->reverse_active_energy); - ESP_LOGCONFIG(TAG, " Calibration:"); - ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_a_->current_gain_calibration); - ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_a_->voltage_gain_calibration); - ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_a_->power_gain_calibration); - ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_a_->phase_angle_calibration); + ESP_LOGCONFIG(TAG, + " Calibration:\n" + " Current: %" PRId32 "\n" + " Voltage: %" PRId32 "\n" + " Power: %" PRId32 "\n" + " Phase Angle: %u", + this->channel_a_->current_gain_calibration, this->channel_a_->voltage_gain_calibration, + this->channel_a_->power_gain_calibration, this->channel_a_->phase_angle_calibration); } if (this->channel_b_ != nullptr) { @@ -193,11 +196,14 @@ void ADE7880::dump_config() { LOG_SENSOR(" ", "Power Factor", this->channel_b_->power_factor); LOG_SENSOR(" ", "Forward Active Energy", this->channel_b_->forward_active_energy); LOG_SENSOR(" ", "Reverse Active Energy", this->channel_b_->reverse_active_energy); - ESP_LOGCONFIG(TAG, " Calibration:"); - ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_b_->current_gain_calibration); - ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_b_->voltage_gain_calibration); - ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_b_->power_gain_calibration); - ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_b_->phase_angle_calibration); + ESP_LOGCONFIG(TAG, + " Calibration:\n" + " Current: %" PRId32 "\n" + " Voltage: %" PRId32 "\n" + " Power: %" PRId32 "\n" + " Phase Angle: %u", + this->channel_b_->current_gain_calibration, this->channel_b_->voltage_gain_calibration, + this->channel_b_->power_gain_calibration, this->channel_b_->phase_angle_calibration); } if (this->channel_c_ != nullptr) { @@ -209,18 +215,23 @@ void ADE7880::dump_config() { LOG_SENSOR(" ", "Power Factor", this->channel_c_->power_factor); LOG_SENSOR(" ", "Forward Active Energy", this->channel_c_->forward_active_energy); LOG_SENSOR(" ", "Reverse Active Energy", this->channel_c_->reverse_active_energy); - ESP_LOGCONFIG(TAG, " Calibration:"); - ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_c_->current_gain_calibration); - ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_c_->voltage_gain_calibration); - ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_c_->power_gain_calibration); - ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_c_->phase_angle_calibration); + ESP_LOGCONFIG(TAG, + " Calibration:\n" + " Current: %" PRId32 "\n" + " Voltage: %" PRId32 "\n" + " Power: %" PRId32 "\n" + " Phase Angle: %u", + this->channel_c_->current_gain_calibration, this->channel_c_->voltage_gain_calibration, + this->channel_c_->power_gain_calibration, this->channel_c_->phase_angle_calibration); } if (this->channel_n_ != nullptr) { ESP_LOGCONFIG(TAG, " Neutral:"); LOG_SENSOR(" ", "Current", this->channel_n_->current); - ESP_LOGCONFIG(TAG, " Calibration:"); - ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_n_->current_gain_calibration); + ESP_LOGCONFIG(TAG, + " Calibration:\n" + " Current: %" PRId32, + this->channel_n_->current_gain_calibration); } LOG_I2C_DEVICE(this); diff --git a/esphome/components/ade7953_base/ade7953_base.cpp b/esphome/components/ade7953_base/ade7953_base.cpp index 2511b4e04c..5f5fdd27ee 100644 --- a/esphome/components/ade7953_base/ade7953_base.cpp +++ b/esphome/components/ade7953_base/ade7953_base.cpp @@ -58,15 +58,18 @@ void ADE7953::dump_config() { LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_); LOG_SENSOR(" ", "Rective Power A Sensor", this->reactive_power_a_sensor_); LOG_SENSOR(" ", "Reactive Power B Sensor", this->reactive_power_b_sensor_); - ESP_LOGCONFIG(TAG, " USE_ACC_ENERGY_REGS: %d", this->use_acc_energy_regs_); - ESP_LOGCONFIG(TAG, " PGA_V_8: 0x%X", pga_v_); - ESP_LOGCONFIG(TAG, " PGA_IA_8: 0x%X", pga_ia_); - ESP_LOGCONFIG(TAG, " PGA_IB_8: 0x%X", pga_ib_); - ESP_LOGCONFIG(TAG, " VGAIN_32: 0x%08jX", (uintmax_t) vgain_); - ESP_LOGCONFIG(TAG, " AIGAIN_32: 0x%08jX", (uintmax_t) aigain_); - ESP_LOGCONFIG(TAG, " BIGAIN_32: 0x%08jX", (uintmax_t) bigain_); - ESP_LOGCONFIG(TAG, " AWGAIN_32: 0x%08jX", (uintmax_t) awgain_); - ESP_LOGCONFIG(TAG, " BWGAIN_32: 0x%08jX", (uintmax_t) bwgain_); + ESP_LOGCONFIG(TAG, + " USE_ACC_ENERGY_REGS: %d\n" + " PGA_V_8: 0x%X\n" + " PGA_IA_8: 0x%X\n" + " PGA_IB_8: 0x%X\n" + " VGAIN_32: 0x%08jX\n" + " AIGAIN_32: 0x%08jX\n" + " BIGAIN_32: 0x%08jX\n" + " AWGAIN_32: 0x%08jX\n" + " BWGAIN_32: 0x%08jX", + this->use_acc_energy_regs_, pga_v_, pga_ia_, pga_ib_, (uintmax_t) vgain_, (uintmax_t) aigain_, + (uintmax_t) bigain_, (uintmax_t) awgain_, (uintmax_t) bwgain_); } #define ADE_PUBLISH_(name, val, factor) \ diff --git a/esphome/components/ade7953_i2c/ade7953_i2c.cpp b/esphome/components/ade7953_i2c/ade7953_i2c.cpp index ae381824db..59c2254d44 100644 --- a/esphome/components/ade7953_i2c/ade7953_i2c.cpp +++ b/esphome/components/ade7953_i2c/ade7953_i2c.cpp @@ -1,6 +1,6 @@ #include "ade7953_i2c.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ade7953_i2c { diff --git a/esphome/components/ade7953_spi/ade7953_spi.cpp b/esphome/components/ade7953_spi/ade7953_spi.cpp index 77a2a8adc7..6b16d933a2 100644 --- a/esphome/components/ade7953_spi/ade7953_spi.cpp +++ b/esphome/components/ade7953_spi/ade7953_spi.cpp @@ -1,6 +1,6 @@ #include "ade7953_spi.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ade7953_spi { diff --git a/esphome/components/ads1115/ads1115.cpp b/esphome/components/ads1115/ads1115.cpp index c05064383c..11a5663ed1 100644 --- a/esphome/components/ads1115/ads1115.cpp +++ b/esphome/components/ads1115/ads1115.cpp @@ -10,15 +10,13 @@ static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00; static const uint8_t ADS1115_REGISTER_CONFIG = 0x01; void ADS1115Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up ADS1115..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint16_t value; if (!this->read_byte_16(ADS1115_REGISTER_CONVERSION, &value)) { this->mark_failed(); return; } - ESP_LOGCONFIG(TAG, "Configuring ADS1115..."); - uint16_t config = 0; // Clear single-shot bit // 0b0xxxxxxxxxxxxxxx @@ -68,10 +66,10 @@ void ADS1115Component::setup() { this->prev_config_ = config; } void ADS1115Component::dump_config() { - ESP_LOGCONFIG(TAG, "Setting up ADS1115..."); + ESP_LOGCONFIG(TAG, "ADS1115:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with ADS1115 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, diff --git a/esphome/components/ads1118/ads1118.cpp b/esphome/components/ads1118/ads1118.cpp index 7b9d0cc36b..1daa8fdfd4 100644 --- a/esphome/components/ads1118/ads1118.cpp +++ b/esphome/components/ads1118/ads1118.cpp @@ -1,4 +1,5 @@ #include "ads1118.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -8,7 +9,7 @@ static const char *const TAG = "ads1118"; static const uint8_t ADS1118_DATA_RATE_860_SPS = 0b111; void ADS1118::setup() { - ESP_LOGCONFIG(TAG, "Setting up ads1118"); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); this->config_ = 0; diff --git a/esphome/components/ags10/ags10.cpp b/esphome/components/ags10/ags10.cpp index 422380da83..797a07afa5 100644 --- a/esphome/components/ags10/ags10.cpp +++ b/esphome/components/ags10/ags10.cpp @@ -1,4 +1,5 @@ #include "ags10.h" +#include "esphome/core/helpers.h" #include @@ -23,7 +24,7 @@ static const uint16_t ZP_CURRENT = 0x0000; static const uint16_t ZP_DEFAULT = 0xFFFF; void AGS10Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up ags10..."); + ESP_LOGCONFIG(TAG, "Running setup"); auto version = this->read_version_(); if (version) { @@ -65,7 +66,7 @@ void AGS10Component::dump_config() { case NONE: break; case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Communication with AGS10 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case CRC_CHECK_FAILED: ESP_LOGE(TAG, "The crc check failed"); diff --git a/esphome/components/aht10/aht10.cpp b/esphome/components/aht10/aht10.cpp index 441c1ac9df..7f17e1c0d6 100644 --- a/esphome/components/aht10/aht10.cpp +++ b/esphome/components/aht10/aht10.cpp @@ -13,8 +13,9 @@ // results making successive requests; the current implementation makes 3 attempts with a delay of 30ms each time. #include "aht10.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace aht10 { @@ -34,57 +35,59 @@ static const uint8_t AHT10_INIT_ATTEMPTS = 10; static const uint8_t AHT10_STATUS_BUSY = 0x80; +static const float AHT10_DIVISOR = 1048576.0f; // 2^20, used for temperature and humidity calculations + void AHT10Component::setup() { + ESP_LOGCONFIG(TAG, "Running setup"); + if (this->write(AHT10_SOFTRESET_CMD, sizeof(AHT10_SOFTRESET_CMD)) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Reset AHT10 failed!"); + ESP_LOGE(TAG, "Reset failed"); } delay(AHT10_SOFTRESET_DELAY); i2c::ErrorCode error_code = i2c::ERROR_INVALID_ARGUMENT; switch (this->variant_) { case AHT10Variant::AHT20: - ESP_LOGCONFIG(TAG, "Setting up AHT20"); error_code = this->write(AHT20_INITIALIZE_CMD, sizeof(AHT20_INITIALIZE_CMD)); break; case AHT10Variant::AHT10: - ESP_LOGCONFIG(TAG, "Setting up AHT10"); error_code = this->write(AHT10_INITIALIZE_CMD, sizeof(AHT10_INITIALIZE_CMD)); break; } if (error_code != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Communication with AHT10 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); return; } + uint8_t cal_attempts = 0; uint8_t data = AHT10_STATUS_BUSY; - int cal_attempts = 0; while (data & AHT10_STATUS_BUSY) { delay(AHT10_DEFAULT_DELAY); if (this->read(&data, 1) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Communication with AHT10 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); return; } ++cal_attempts; if (cal_attempts > AHT10_INIT_ATTEMPTS) { - ESP_LOGE(TAG, "AHT10 initialization timed out!"); + ESP_LOGE(TAG, "Initialization timed out"); this->mark_failed(); return; } } if ((data & 0x68) != 0x08) { // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED - ESP_LOGE(TAG, "AHT10 initialization failed!"); + ESP_LOGE(TAG, "Initialization failed"); this->mark_failed(); return; } - ESP_LOGV(TAG, "AHT10 initialization"); + ESP_LOGV(TAG, "Initialization complete"); } void AHT10Component::restart_read_() { if (this->read_count_ == AHT10_ATTEMPTS) { this->read_count_ = 0; - this->status_set_error("Measurements reading timed-out!"); + this->status_set_error("Reading timed out"); return; } this->read_count_++; @@ -97,24 +100,24 @@ void AHT10Component::read_data_() { ESP_LOGD(TAG, "Read attempt %d at %ums", this->read_count_, (unsigned) (millis() - this->start_time_)); } if (this->read(data, 6) != i2c::ERROR_OK) { - this->status_set_warning("AHT10 read failed, retrying soon"); + this->status_set_warning("Read failed, will retry"); this->restart_read_(); return; } if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy - ESP_LOGD(TAG, "AHT10 is busy, waiting..."); + ESP_LOGD(TAG, "Device busy, will retry"); this->restart_read_(); return; } if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) { - // Unrealistic humidity (0x0) + // Invalid humidity (0x0) if (this->humidity_sensor_ == nullptr) { - ESP_LOGV(TAG, "ATH10 Unrealistic humidity (0x0), but humidity is not required"); + ESP_LOGV(TAG, "Invalid humidity (reading not required)"); } else { - ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying..."); + ESP_LOGD(TAG, "Invalid humidity, retrying"); if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) { - this->status_set_warning("Communication with AHT10 failed!"); + this->status_set_warning(ESP_LOG_MSG_COMM_FAIL); } this->restart_read_(); return; @@ -123,22 +126,17 @@ void AHT10Component::read_data_() { if (this->read_count_ > 1) { ESP_LOGD(TAG, "Success at %ums", (unsigned) (millis() - this->start_time_)); } - uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]; - uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4; + uint32_t raw_temperature = encode_uint24(data[3] & 0xF, data[4], data[5]); + uint32_t raw_humidity = encode_uint24(data[1], data[2], data[3]) >> 4; if (this->temperature_sensor_ != nullptr) { - float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f; + float temperature = ((200.0f * static_cast(raw_temperature)) / AHT10_DIVISOR) - 50.0f; this->temperature_sensor_->publish_state(temperature); } if (this->humidity_sensor_ != nullptr) { - float humidity; - if (raw_humidity == 0) { // unrealistic value - humidity = NAN; - } else { - humidity = (float) raw_humidity * 100.0f / 1048576.0f; - } + float humidity = raw_humidity == 0 ? NAN : static_cast(raw_humidity) * 100.0f / AHT10_DIVISOR; if (std::isnan(humidity)) { - ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum"); + ESP_LOGW(TAG, "Invalid humidity reading (0%%), "); } this->humidity_sensor_->publish_state(humidity); } @@ -150,7 +148,7 @@ void AHT10Component::update() { return; this->start_time_ = millis(); if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) { - this->status_set_warning("Communication with AHT10 failed!"); + this->status_set_warning(ESP_LOG_MSG_COMM_FAIL); return; } this->restart_read_(); @@ -162,7 +160,7 @@ void AHT10Component::dump_config() { ESP_LOGCONFIG(TAG, "AHT10:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with AHT10 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); diff --git a/esphome/components/aic3204/aic3204.cpp b/esphome/components/aic3204/aic3204.cpp index 0560f2366b..a004fb42ce 100644 --- a/esphome/components/aic3204/aic3204.cpp +++ b/esphome/components/aic3204/aic3204.cpp @@ -17,7 +17,7 @@ static const char *const TAG = "aic3204"; } void AIC3204::setup() { - ESP_LOGCONFIG(TAG, "Setting up AIC3204..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Set register page to 0 ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x00), "Set page 0 failed"); @@ -113,7 +113,7 @@ void AIC3204::dump_config() { LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with AIC3204 failed"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } diff --git a/esphome/components/alarm_control_panel/__init__.py b/esphome/components/alarm_control_panel/__init__.py index 1bcb83bce7..e88050132a 100644 --- a/esphome/components/alarm_control_panel/__init__.py +++ b/esphome/components/alarm_control_panel/__init__.py @@ -235,6 +235,7 @@ async def register_alarm_control_panel(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_alarm_control_panel(var)) + CORE.register_platform_component("alarm_control_panel", var) await setup_alarm_control_panel_core_(var, config) diff --git a/esphome/components/am2315c/am2315c.cpp b/esphome/components/am2315c/am2315c.cpp index 90565de740..cea5263fd6 100644 --- a/esphome/components/am2315c/am2315c.cpp +++ b/esphome/components/am2315c/am2315c.cpp @@ -90,7 +90,7 @@ bool AM2315C::convert_(uint8_t *data, float &humidity, float &temperature) { } void AM2315C::setup() { - ESP_LOGCONFIG(TAG, "Setting up AM2315C..."); + ESP_LOGCONFIG(TAG, "Running setup"); // get status uint8_t status = 0; @@ -188,7 +188,7 @@ void AM2315C::dump_config() { ESP_LOGCONFIG(TAG, "AM2315C:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with AM2315C failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); diff --git a/esphome/components/am2320/am2320.cpp b/esphome/components/am2320/am2320.cpp index ec501f2d36..6400ecef4b 100644 --- a/esphome/components/am2320/am2320.cpp +++ b/esphome/components/am2320/am2320.cpp @@ -34,7 +34,7 @@ void AM2320Component::update() { this->status_clear_warning(); } void AM2320Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up AM2320..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t data[8]; data[0] = 0; data[1] = 4; @@ -47,7 +47,7 @@ void AM2320Component::dump_config() { ESP_LOGD(TAG, "AM2320:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with AM2320 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); diff --git a/esphome/components/am43/am43_base.h b/esphome/components/am43/am43_base.h index e817f161fe..35354af9ed 100644 --- a/esphome/components/am43/am43_base.h +++ b/esphome/components/am43/am43_base.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace am43 { diff --git a/esphome/components/am43/cover/am43_cover.cpp b/esphome/components/am43/cover/am43_cover.cpp index 93c77ea364..0d49439095 100644 --- a/esphome/components/am43/cover/am43_cover.cpp +++ b/esphome/components/am43/cover/am43_cover.cpp @@ -12,8 +12,10 @@ using namespace esphome::cover; void Am43Component::dump_config() { LOG_COVER("", "AM43 Cover", this); - ESP_LOGCONFIG(TAG, " Device Pin: %d", this->pin_); - ESP_LOGCONFIG(TAG, " Invert Position: %d", (int) this->invert_position_); + ESP_LOGCONFIG(TAG, + " Device Pin: %d\n" + " Invert Position: %d", + this->pin_, (int) this->invert_position_); } void Am43Component::setup() { diff --git a/esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp b/esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp index 8dcbb2ac4b..f83f2aff08 100644 --- a/esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +++ b/esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp @@ -34,8 +34,10 @@ void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) { void AnalogThresholdBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this); LOG_SENSOR(" ", "Sensor", this->sensor_); - ESP_LOGCONFIG(TAG, " Upper threshold: %.11f", this->upper_threshold_.value()); - ESP_LOGCONFIG(TAG, " Lower threshold: %.11f", this->lower_threshold_.value()); + ESP_LOGCONFIG(TAG, + " Upper threshold: %.11f\n" + " Lower threshold: %.11f", + this->upper_threshold_.value(), this->lower_threshold_.value()); } } // namespace analog_threshold diff --git a/esphome/components/apds9306/apds9306.cpp b/esphome/components/apds9306/apds9306.cpp index bbb3ba1910..9799f54d3d 100644 --- a/esphome/components/apds9306/apds9306.cpp +++ b/esphome/components/apds9306/apds9306.cpp @@ -54,7 +54,7 @@ enum { // APDS9306 registers } void APDS9306::setup() { - ESP_LOGCONFIG(TAG, "Setting up APDS9306..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t id; if (!this->read_byte(APDS9306_PART_ID, &id)) { // Part ID register @@ -97,7 +97,7 @@ void APDS9306::dump_config() { if (this->is_failed()) { switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Communication with APDS9306 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case WRONG_ID: ESP_LOGE(TAG, "APDS9306 has invalid id!"); @@ -108,9 +108,12 @@ void APDS9306::dump_config() { } } - ESP_LOGCONFIG(TAG, " Gain: %u", AMBIENT_LIGHT_GAIN_VALUES[this->gain_]); - ESP_LOGCONFIG(TAG, " Measurement rate: %u", MEASUREMENT_RATE_VALUES[this->measurement_rate_]); - ESP_LOGCONFIG(TAG, " Measurement Resolution/Bit width: %d", MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]); + ESP_LOGCONFIG(TAG, + " Gain: %u\n" + " Measurement rate: %u\n" + " Measurement Resolution/Bit width: %d", + AMBIENT_LIGHT_GAIN_VALUES[this->gain_], MEASUREMENT_RATE_VALUES[this->measurement_rate_], + MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/apds9960/apds9960.cpp b/esphome/components/apds9960/apds9960.cpp index d91378afee..4a6ce371e5 100644 --- a/esphome/components/apds9960/apds9960.cpp +++ b/esphome/components/apds9960/apds9960.cpp @@ -15,7 +15,7 @@ static const char *const TAG = "apds9960"; #define APDS9960_WRITE_BYTE(reg, value) APDS9960_ERROR_CHECK(this->write_byte(reg, value)); void APDS9960::setup() { - ESP_LOGCONFIG(TAG, "Setting up APDS9960..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t id; if (!this->read_byte(0x92, &id)) { // ID register this->error_code_ = COMMUNICATION_FAILED; @@ -141,7 +141,7 @@ void APDS9960::dump_config() { if (this->is_failed()) { switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Communication with APDS9960 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case WRONG_ID: ESP_LOGE(TAG, "APDS9960 has invalid id!"); diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 4b63c76fba..bd131ef8de 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -49,6 +49,7 @@ SERVICE_ARG_NATIVE_TYPES = { "string[]": cg.std_vector.template(cg.std_string), } CONF_ENCRYPTION = "encryption" +CONF_BATCH_DELAY = "batch_delay" def validate_encryption_key(value): @@ -109,6 +110,9 @@ CONFIG_SCHEMA = cv.All( ): ACTIONS_SCHEMA, cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA, cv.Optional(CONF_ENCRYPTION): _encryption_schema, + cv.Optional( + CONF_BATCH_DELAY, default="100ms" + ): cv.positive_time_period_milliseconds, cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation( single=True ), @@ -129,6 +133,7 @@ async def to_code(config): cg.add(var.set_port(config[CONF_PORT])) cg.add(var.set_password(config[CONF_PASSWORD])) cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) + cg.add(var.set_batch_delay(config[CONF_BATCH_DELAY])) for conf in config.get(CONF_ACTIONS, []): template_args = [] diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 1fdf4e1339..b23652a982 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -266,6 +266,7 @@ enum EntityCategory { // ==================== BINARY SENSOR ==================== message ListEntitiesBinarySensorResponse { option (id) = 12; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_BINARY_SENSOR"; @@ -282,6 +283,7 @@ message ListEntitiesBinarySensorResponse { } message BinarySensorStateResponse { option (id) = 21; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_BINARY_SENSOR"; option (no_delay) = true; @@ -296,6 +298,7 @@ message BinarySensorStateResponse { // ==================== COVER ==================== message ListEntitiesCoverResponse { option (id) = 13; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_COVER"; @@ -325,6 +328,7 @@ enum CoverOperation { } message CoverStateResponse { option (id) = 22; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_COVER"; option (no_delay) = true; @@ -367,6 +371,7 @@ message CoverCommandRequest { // ==================== FAN ==================== message ListEntitiesFanResponse { option (id) = 14; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_FAN"; @@ -395,6 +400,7 @@ enum FanDirection { } message FanStateResponse { option (id) = 23; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_FAN"; option (no_delay) = true; @@ -432,7 +438,8 @@ message FanCommandRequest { enum ColorMode { COLOR_MODE_UNKNOWN = 0; COLOR_MODE_ON_OFF = 1; - COLOR_MODE_BRIGHTNESS = 2; + COLOR_MODE_LEGACY_BRIGHTNESS = 2; + COLOR_MODE_BRIGHTNESS = 3; COLOR_MODE_WHITE = 7; COLOR_MODE_COLOR_TEMPERATURE = 11; COLOR_MODE_COLD_WARM_WHITE = 19; @@ -443,6 +450,7 @@ enum ColorMode { } message ListEntitiesLightResponse { option (id) = 15; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_LIGHT"; @@ -466,6 +474,7 @@ message ListEntitiesLightResponse { } message LightStateResponse { option (id) = 24; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_LIGHT"; option (no_delay) = true; @@ -535,6 +544,7 @@ enum SensorLastResetType { message ListEntitiesSensorResponse { option (id) = 16; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SENSOR"; @@ -556,6 +566,7 @@ message ListEntitiesSensorResponse { } message SensorStateResponse { option (id) = 25; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SENSOR"; option (no_delay) = true; @@ -570,6 +581,7 @@ message SensorStateResponse { // ==================== SWITCH ==================== message ListEntitiesSwitchResponse { option (id) = 17; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SWITCH"; @@ -586,6 +598,7 @@ message ListEntitiesSwitchResponse { } message SwitchStateResponse { option (id) = 26; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SWITCH"; option (no_delay) = true; @@ -606,6 +619,7 @@ message SwitchCommandRequest { // ==================== TEXT SENSOR ==================== message ListEntitiesTextSensorResponse { option (id) = 18; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_TEXT_SENSOR"; @@ -621,6 +635,7 @@ message ListEntitiesTextSensorResponse { } message TextSensorStateResponse { option (id) = 27; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_TEXT_SENSOR"; option (no_delay) = true; @@ -788,6 +803,7 @@ message ExecuteServiceRequest { // ==================== CAMERA ==================== message ListEntitiesCameraResponse { option (id) = 43; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_ESP32_CAMERA"; @@ -868,6 +884,7 @@ enum ClimatePreset { } message ListEntitiesClimateResponse { option (id) = 46; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_CLIMATE"; @@ -902,6 +919,7 @@ message ListEntitiesClimateResponse { } message ClimateStateResponse { option (id) = 47; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_CLIMATE"; option (no_delay) = true; @@ -963,6 +981,7 @@ enum NumberMode { } message ListEntitiesNumberResponse { option (id) = 49; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_NUMBER"; @@ -983,6 +1002,7 @@ message ListEntitiesNumberResponse { } message NumberStateResponse { option (id) = 50; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_NUMBER"; option (no_delay) = true; @@ -1006,6 +1026,7 @@ message NumberCommandRequest { // ==================== SELECT ==================== message ListEntitiesSelectResponse { option (id) = 52; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SELECT"; @@ -1021,6 +1042,7 @@ message ListEntitiesSelectResponse { } message SelectStateResponse { option (id) = 53; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SELECT"; option (no_delay) = true; @@ -1044,6 +1066,7 @@ message SelectCommandRequest { // ==================== SIREN ==================== message ListEntitiesSirenResponse { option (id) = 55; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SIREN"; @@ -1061,6 +1084,7 @@ message ListEntitiesSirenResponse { } message SirenStateResponse { option (id) = 56; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_SIREN"; option (no_delay) = true; @@ -1101,6 +1125,7 @@ enum LockCommand { } message ListEntitiesLockResponse { option (id) = 58; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_LOCK"; @@ -1122,6 +1147,7 @@ message ListEntitiesLockResponse { } message LockStateResponse { option (id) = 59; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_LOCK"; option (no_delay) = true; @@ -1144,6 +1170,7 @@ message LockCommandRequest { // ==================== BUTTON ==================== message ListEntitiesButtonResponse { option (id) = 61; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_BUTTON"; @@ -1195,6 +1222,7 @@ message MediaPlayerSupportedFormat { } message ListEntitiesMediaPlayerResponse { option (id) = 63; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_MEDIA_PLAYER"; @@ -1213,6 +1241,7 @@ message ListEntitiesMediaPlayerResponse { } message MediaPlayerStateResponse { option (id) = 64; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_MEDIA_PLAYER"; option (no_delay) = true; @@ -1614,6 +1643,7 @@ enum VoiceAssistantEvent { VOICE_ASSISTANT_STT_VAD_END = 12; VOICE_ASSISTANT_TTS_STREAM_START = 98; VOICE_ASSISTANT_TTS_STREAM_END = 99; + VOICE_ASSISTANT_INTENT_PROGRESS = 100; } message VoiceAssistantEventData { @@ -1734,6 +1764,7 @@ enum AlarmControlPanelStateCommand { message ListEntitiesAlarmControlPanelResponse { option (id) = 94; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_ALARM_CONTROL_PANEL"; @@ -1751,6 +1782,7 @@ message ListEntitiesAlarmControlPanelResponse { message AlarmControlPanelStateResponse { option (id) = 95; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_ALARM_CONTROL_PANEL"; option (no_delay) = true; @@ -1775,6 +1807,7 @@ enum TextMode { } message ListEntitiesTextResponse { option (id) = 97; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_TEXT"; @@ -1793,6 +1826,7 @@ message ListEntitiesTextResponse { } message TextStateResponse { option (id) = 98; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_TEXT"; option (no_delay) = true; @@ -1817,6 +1851,7 @@ message TextCommandRequest { // ==================== DATETIME DATE ==================== message ListEntitiesDateResponse { option (id) = 100; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_DATE"; @@ -1831,6 +1866,7 @@ message ListEntitiesDateResponse { } message DateStateResponse { option (id) = 101; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_DATE"; option (no_delay) = true; @@ -1858,6 +1894,7 @@ message DateCommandRequest { // ==================== DATETIME TIME ==================== message ListEntitiesTimeResponse { option (id) = 103; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_TIME"; @@ -1872,6 +1909,7 @@ message ListEntitiesTimeResponse { } message TimeStateResponse { option (id) = 104; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_TIME"; option (no_delay) = true; @@ -1899,6 +1937,7 @@ message TimeCommandRequest { // ==================== EVENT ==================== message ListEntitiesEventResponse { option (id) = 107; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_EVENT"; @@ -1916,6 +1955,7 @@ message ListEntitiesEventResponse { } message EventResponse { option (id) = 108; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_EVENT"; @@ -1926,6 +1966,7 @@ message EventResponse { // ==================== VALVE ==================== message ListEntitiesValveResponse { option (id) = 109; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_VALVE"; @@ -1951,6 +1992,7 @@ enum ValveOperation { } message ValveStateResponse { option (id) = 110; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_VALVE"; option (no_delay) = true; @@ -1975,6 +2017,7 @@ message ValveCommandRequest { // ==================== DATETIME DATETIME ==================== message ListEntitiesDateTimeResponse { option (id) = 112; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_DATETIME"; @@ -1989,6 +2032,7 @@ message ListEntitiesDateTimeResponse { } message DateTimeStateResponse { option (id) = 113; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_DATETIME"; option (no_delay) = true; @@ -2012,6 +2056,7 @@ message DateTimeCommandRequest { // ==================== UPDATE ==================== message ListEntitiesUpdateResponse { option (id) = 116; + option (base_class) = "InfoResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_UPDATE"; @@ -2027,6 +2072,7 @@ message ListEntitiesUpdateResponse { } message UpdateStateResponse { option (id) = 117; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_UPDATE"; option (no_delay) = true; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index b4646a2d7d..3e2b7c0154 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "esphome/components/network/util.h" #include "esphome/core/application.h" #include "esphome/core/entity_base.h" @@ -29,40 +31,8 @@ namespace api { static const char *const TAG = "api.connection"; static const int ESP32_CAMERA_STOP_STREAM = 5000; -// helper for allowing only unique entries in the queue -void DeferredMessageQueue::dmq_push_back_with_dedup_(void *source, send_message_t send_message) { - DeferredMessage item(source, send_message); - - auto iter = std::find_if(this->deferred_queue_.begin(), this->deferred_queue_.end(), - [&item](const DeferredMessage &test) -> bool { return test == item; }); - - if (iter != this->deferred_queue_.end()) { - (*iter) = item; - } else { - this->deferred_queue_.push_back(item); - } -} - -void DeferredMessageQueue::process_queue() { - while (!deferred_queue_.empty()) { - DeferredMessage &de = deferred_queue_.front(); - if ((this->api_connection_->*(de.send_message_))(de.source_)) { - // O(n) but memory efficiency is more important than speed here which is why std::vector was chosen - deferred_queue_.erase(deferred_queue_.begin()); - } else { - break; - } - } -} - -void DeferredMessageQueue::defer(void *source, send_message_t send_message) { - this->dmq_push_back_with_dedup_(source, send_message); -} - APIConnection::APIConnection(std::unique_ptr sock, APIServer *parent) - : parent_(parent), deferred_message_queue_(this), initial_state_iterator_(this), list_entities_iterator_(this) { - this->proto_write_buffer_.reserve(64); - + : parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) { #if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE) auto noise_ctx = parent->get_noise_ctx(); if (noise_ctx->has_psk()) { @@ -78,6 +48,9 @@ APIConnection::APIConnection(std::unique_ptr sock, APIServer *pa #error "No frame helper defined" #endif } + +uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); } + void APIConnection::start() { this->last_traffic_ = App.get_loop_component_start_time(); @@ -118,7 +91,7 @@ void APIConnection::loop() { // when network is disconnected force disconnect immediately // don't wait for timeout this->on_fatal_error(); - ESP_LOGW(TAG, "%s: Network unavailable, disconnecting", this->client_combined_info_.c_str()); + ESP_LOGW(TAG, "%s: Network unavailable; disconnecting", this->client_combined_info_.c_str()); return; } if (this->next_close_) { @@ -135,35 +108,41 @@ void APIConnection::loop() { api_error_to_str(err), errno); return; } - ReadPacketBuffer buffer; - err = this->helper_->read_packet(&buffer); - if (err == APIError::WOULD_BLOCK) { - // pass - } else if (err != APIError::OK) { - on_fatal_error(); - if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) { - ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str()); - } else if (err == APIError::CONNECTION_CLOSED) { - ESP_LOGW(TAG, "%s: Connection closed", this->client_combined_info_.c_str()); - } else { - ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err), - errno); - } - return; - } else { - this->last_traffic_ = App.get_loop_component_start_time(); - // read a packet - if (buffer.data_len > 0) { - this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]); - } else { - this->read_message(0, buffer.type, nullptr); - } - if (this->remove_) + + // Check if socket has data ready before attempting to read + if (this->helper_->is_socket_ready()) { + ReadPacketBuffer buffer; + err = this->helper_->read_packet(&buffer); + if (err == APIError::WOULD_BLOCK) { + // pass + } else if (err != APIError::OK) { + on_fatal_error(); + if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) { + ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str()); + } else if (err == APIError::CONNECTION_CLOSED) { + ESP_LOGW(TAG, "%s: Connection closed", this->client_combined_info_.c_str()); + } else { + ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err), + errno); + } return; + } else { + this->last_traffic_ = App.get_loop_component_start_time(); + // read a packet + if (buffer.data_len > 0) { + this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]); + } else { + this->read_message(0, buffer.type, nullptr); + } + if (this->remove_) + return; + } } - if (!this->deferred_message_queue_.empty() && this->helper_->can_write_without_blocking()) { - this->deferred_message_queue_.process_queue(); + // Process deferred batch if scheduled + if (this->deferred_batch_.batch_scheduled && + App.get_loop_component_start_time() - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) { + this->process_batch_(); } if (!this->list_entities_iterator_.completed()) @@ -178,42 +157,30 @@ void APIConnection::loop() { // Disconnect if not responded within 2.5*keepalive if (now - this->last_traffic_ > (KEEPALIVE_TIMEOUT_MS * 5) / 2) { on_fatal_error(); - ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_combined_info_.c_str()); + ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->client_combined_info_.c_str()); } } else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && now > this->next_ping_retry_) { - ESP_LOGVV(TAG, "Sending keepalive PING..."); - this->sent_ping_ = this->send_ping_request(PingRequest()); + ESP_LOGVV(TAG, "Sending keepalive PING"); + this->sent_ping_ = this->send_message(PingRequest()); if (!this->sent_ping_) { this->next_ping_retry_ = now + ping_retry_interval; this->ping_retries_++; + std::string warn_str = str_sprintf("%s: Sending keepalive failed %u time(s);", + this->client_combined_info_.c_str(), this->ping_retries_); if (this->ping_retries_ >= max_ping_retries) { on_fatal_error(); - ESP_LOGE(TAG, "%s: Sending keepalive failed %d time(s). Disconnecting...", this->client_combined_info_.c_str(), - this->ping_retries_); + ESP_LOGE(TAG, "%s disconnecting", warn_str.c_str()); } else if (this->ping_retries_ >= 10) { - ESP_LOGW(TAG, "%s: Sending keepalive failed %d time(s), will retry in %d ms", - this->client_combined_info_.c_str(), this->ping_retries_, ping_retry_interval); + ESP_LOGW(TAG, "%s retrying in %u ms", warn_str.c_str(), ping_retry_interval); } else { - ESP_LOGD(TAG, "%s: Sending keepalive failed %d time(s), will retry in %d ms", - this->client_combined_info_.c_str(), this->ping_retries_, ping_retry_interval); + ESP_LOGD(TAG, "%s retrying in %u ms", warn_str.c_str(), ping_retry_interval); } } } #ifdef USE_ESP32_CAMERA if (this->image_reader_.available() && this->helper_->can_write_without_blocking()) { - // Message will use 8 more bytes than the minimum size, and typical - // MTU is 1500. Sometimes users will see as low as 1460 MTU. - // If its IPv6 the header is 40 bytes, and if its IPv4 - // the header is 20 bytes. So we have 1460 - 40 = 1420 bytes - // available for the payload. But we also need to add the size of - // the protobuf overhead, which is 8 bytes. - // - // To be safe we pick 1390 bytes as the maximum size - // to send in one go. This is the maximum size of a single packet - // that can be sent over the network. - // This is to avoid fragmentation of the packet. - uint32_t to_send = std::min((size_t) 1390, this->image_reader_.available()); + uint32_t to_send = std::min((size_t) MAX_PACKET_SIZE, this->image_reader_.available()); bool done = this->image_reader_.available() == to_send; uint32_t msg_size = 0; ProtoSize::add_fixed_field<4>(msg_size, 1, true); @@ -251,7 +218,7 @@ void APIConnection::loop() { resp.entity_id = it.entity_id; resp.attribute = it.attribute.value(); resp.once = it.once; - if (this->send_subscribe_home_assistant_state_response(resp)) { + if (this->send_message(resp)) { state_subs_at_++; } } @@ -266,54 +233,100 @@ DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) { // remote initiated disconnect_client // don't close yet, we still need to send the disconnect response // close will happen on next loop - ESP_LOGD(TAG, "%s requested disconnected", this->client_combined_info_.c_str()); + ESP_LOGD(TAG, "%s disconnected", this->client_combined_info_.c_str()); this->next_close_ = true; DisconnectResponse resp; return resp; } void APIConnection::on_disconnect_response(const DisconnectResponse &value) { - // pass + this->helper_->close(); + this->remove_ = true; +} + +// Encodes a message to the buffer and returns the total number of bytes used, +// including header and footer overhead. Returns 0 if the message doesn't fit. +uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn, + uint32_t remaining_size, bool is_single) { + // Calculate size + uint32_t calculated_size = 0; + msg.calculate_size(calculated_size); + + // Cache frame sizes to avoid repeated virtual calls + const uint8_t header_padding = conn->helper_->frame_header_padding(); + const uint8_t footer_size = conn->helper_->frame_footer_size(); + + // Calculate total size with padding for buffer allocation + size_t total_calculated_size = calculated_size + header_padding + footer_size; + + // Check if it fits + if (total_calculated_size > remaining_size) { + return 0; // Doesn't fit + } + + // Allocate buffer space - pass payload size, allocation functions add header/footer space + ProtoWriteBuffer buffer = is_single ? conn->allocate_single_message_buffer(calculated_size) + : conn->allocate_batch_message_buffer(calculated_size); + + // Get buffer size after allocation (which includes header padding) + std::vector &shared_buf = conn->parent_->get_shared_buffer_ref(); + size_t size_before_encode = shared_buf.size(); + + // Encode directly into buffer + msg.encode(buffer); + + // Calculate actual encoded size (not including header that was already added) + size_t actual_payload_size = shared_buf.size() - size_before_encode; + + // Return actual total size (header + actual payload + footer) + size_t actual_total_size = header_padding + actual_payload_size + footer_size; + + // Verify that calculate_size() returned the correct value + assert(calculated_size == actual_payload_size); + return static_cast(actual_total_size); } #ifdef USE_BINARY_SENSOR -bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state) { - return this->send_state_with_value_(binary_sensor, &APIConnection::try_send_binary_sensor_state_, - &APIConnection::try_send_binary_sensor_state_, state); +bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor) { + return this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_state, + BinarySensorStateResponse::MESSAGE_TYPE); } void APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) { - this->send_info_(static_cast(binary_sensor), - reinterpret_cast(&APIConnection::try_send_binary_sensor_info_)); + this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_info, + ListEntitiesBinarySensorResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor) { - return this->try_send_binary_sensor_state_(binary_sensor, binary_sensor->state); + +uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *binary_sensor = static_cast(entity); + BinarySensorStateResponse resp; + resp.state = binary_sensor->state; + resp.missing_state = !binary_sensor->has_state(); + fill_entity_state_base(binary_sensor, resp); + return encode_message_to_buffer(resp, BinarySensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor, bool state) { - BinarySensorStateResponse msg; - msg.state = state; - msg.missing_state = !binary_sensor->has_state(); - msg.key = binary_sensor->get_object_id_hash(); - return this->send_binary_sensor_state_response(msg); -} -bool APIConnection::try_send_binary_sensor_info_(binary_sensor::BinarySensor *binary_sensor) { + +uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *binary_sensor = static_cast(entity); ListEntitiesBinarySensorResponse msg; msg.device_class = binary_sensor->get_device_class(); msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor(); msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor); - return this->try_send_entity_info_(static_cast(binary_sensor), msg, - &APIConnection::send_list_entities_binary_sensor_response); + fill_entity_info_base(binary_sensor, msg); + return encode_message_to_buffer(msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } #endif #ifdef USE_COVER bool APIConnection::send_cover_state(cover::Cover *cover) { - return this->send_state_(static_cast(cover), - reinterpret_cast(&APIConnection::try_send_cover_state_)); + return this->schedule_message_(cover, &APIConnection::try_send_cover_state, CoverStateResponse::MESSAGE_TYPE); } void APIConnection::send_cover_info(cover::Cover *cover) { - this->send_info_(static_cast(cover), - reinterpret_cast(&APIConnection::try_send_cover_info_)); + this->schedule_message_(cover, &APIConnection::try_send_cover_info, ListEntitiesCoverResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_cover_state_(cover::Cover *cover) { +uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *cover = static_cast(entity); CoverStateResponse msg; auto traits = cover->get_traits(); msg.legacy_state = @@ -322,10 +335,12 @@ bool APIConnection::try_send_cover_state_(cover::Cover *cover) { if (traits.get_supports_tilt()) msg.tilt = cover->tilt; msg.current_operation = static_cast(cover->current_operation); - msg.key = cover->get_object_id_hash(); - return this->send_cover_state_response(msg); + fill_entity_state_base(cover, msg); + return encode_message_to_buffer(msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_cover_info_(cover::Cover *cover) { +uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *cover = static_cast(entity); ListEntitiesCoverResponse msg; auto traits = cover->get_traits(); msg.assumed_state = traits.get_is_assumed_state(); @@ -334,8 +349,8 @@ bool APIConnection::try_send_cover_info_(cover::Cover *cover) { msg.supports_stop = traits.get_supports_stop(); msg.device_class = cover->get_device_class(); msg.unique_id = get_default_unique_id("cover", cover); - return this->try_send_entity_info_(static_cast(cover), msg, - &APIConnection::send_list_entities_cover_response); + fill_entity_info_base(cover, msg); + return encode_message_to_buffer(msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::cover_command(const CoverCommandRequest &msg) { cover::Cover *cover = App.get_cover_by_key(msg.key); @@ -368,14 +383,14 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) { #ifdef USE_FAN bool APIConnection::send_fan_state(fan::Fan *fan) { - return this->send_state_(static_cast(fan), - reinterpret_cast(&APIConnection::try_send_fan_state_)); + return this->schedule_message_(fan, &APIConnection::try_send_fan_state, FanStateResponse::MESSAGE_TYPE); } void APIConnection::send_fan_info(fan::Fan *fan) { - this->send_info_(static_cast(fan), - reinterpret_cast(&APIConnection::try_send_fan_info_)); + this->schedule_message_(fan, &APIConnection::try_send_fan_info, ListEntitiesFanResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_fan_state_(fan::Fan *fan) { +uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *fan = static_cast(entity); FanStateResponse msg; auto traits = fan->get_traits(); msg.state = fan->state; @@ -388,10 +403,12 @@ bool APIConnection::try_send_fan_state_(fan::Fan *fan) { msg.direction = static_cast(fan->direction); if (traits.supports_preset_modes()) msg.preset_mode = fan->preset_mode; - msg.key = fan->get_object_id_hash(); - return this->send_fan_state_response(msg); + fill_entity_state_base(fan, msg); + return encode_message_to_buffer(msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_fan_info_(fan::Fan *fan) { +uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *fan = static_cast(entity); ListEntitiesFanResponse msg; auto traits = fan->get_traits(); msg.supports_oscillation = traits.supports_oscillation(); @@ -401,8 +418,8 @@ bool APIConnection::try_send_fan_info_(fan::Fan *fan) { for (auto const &preset : traits.supported_preset_modes()) msg.supported_preset_modes.push_back(preset); msg.unique_id = get_default_unique_id("fan", fan); - return this->try_send_entity_info_(static_cast(fan), msg, - &APIConnection::send_list_entities_fan_response); + fill_entity_info_base(fan, msg); + return encode_message_to_buffer(msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::fan_command(const FanCommandRequest &msg) { fan::Fan *fan = App.get_fan_by_key(msg.key); @@ -428,14 +445,14 @@ void APIConnection::fan_command(const FanCommandRequest &msg) { #ifdef USE_LIGHT bool APIConnection::send_light_state(light::LightState *light) { - return this->send_state_(static_cast(light), - reinterpret_cast(&APIConnection::try_send_light_state_)); + return this->schedule_message_(light, &APIConnection::try_send_light_state, LightStateResponse::MESSAGE_TYPE); } void APIConnection::send_light_info(light::LightState *light) { - this->send_info_(static_cast(light), - reinterpret_cast(&APIConnection::try_send_light_info_)); + this->schedule_message_(light, &APIConnection::try_send_light_info, ListEntitiesLightResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_light_state_(light::LightState *light) { +uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *light = static_cast(entity); LightStateResponse resp; auto traits = light->get_traits(); auto values = light->remote_values; @@ -453,10 +470,12 @@ bool APIConnection::try_send_light_state_(light::LightState *light) { resp.warm_white = values.get_warm_white(); if (light->supports_effects()) resp.effect = light->get_effect_name(); - resp.key = light->get_object_id_hash(); - return this->send_light_state_response(resp); + fill_entity_state_base(light, resp); + return encode_message_to_buffer(resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_light_info_(light::LightState *light) { +uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *light = static_cast(entity); ListEntitiesLightResponse msg; auto traits = light->get_traits(); for (auto mode : traits.get_supported_color_modes()) @@ -479,8 +498,8 @@ bool APIConnection::try_send_light_info_(light::LightState *light) { } } msg.unique_id = get_default_unique_id("light", light); - return this->try_send_entity_info_(static_cast(light), msg, - &APIConnection::send_list_entities_light_response); + fill_entity_info_base(light, msg); + return encode_message_to_buffer(msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::light_command(const LightCommandRequest &msg) { light::LightState *light = App.get_light_by_key(msg.key); @@ -520,26 +539,26 @@ void APIConnection::light_command(const LightCommandRequest &msg) { #endif #ifdef USE_SENSOR -bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) { - return this->send_state_with_value_(sensor, &APIConnection::try_send_sensor_state_, - &APIConnection::try_send_sensor_state_, state); +bool APIConnection::send_sensor_state(sensor::Sensor *sensor) { + return this->schedule_message_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE); } void APIConnection::send_sensor_info(sensor::Sensor *sensor) { - this->send_info_(static_cast(sensor), - reinterpret_cast(&APIConnection::try_send_sensor_info_)); + this->schedule_message_(sensor, &APIConnection::try_send_sensor_info, ListEntitiesSensorResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_sensor_state_(sensor::Sensor *sensor) { - return this->try_send_sensor_state_(sensor, sensor->state); -} -bool APIConnection::try_send_sensor_state_(sensor::Sensor *sensor, float state) { - SensorStateResponse resp; - resp.state = state; - resp.missing_state = !sensor->has_state(); - resp.key = sensor->get_object_id_hash(); - return this->send_sensor_state_response(resp); +uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *sensor = static_cast(entity); + SensorStateResponse resp; + resp.state = sensor->state; + resp.missing_state = !sensor->has_state(); + fill_entity_state_base(sensor, resp); + return encode_message_to_buffer(resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_sensor_info_(sensor::Sensor *sensor) { + +uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *sensor = static_cast(entity); ListEntitiesSensorResponse msg; msg.unit_of_measurement = sensor->get_unit_of_measurement(); msg.accuracy_decimals = sensor->get_accuracy_decimals(); @@ -549,37 +568,37 @@ bool APIConnection::try_send_sensor_info_(sensor::Sensor *sensor) { msg.unique_id = sensor->unique_id(); if (msg.unique_id.empty()) msg.unique_id = get_default_unique_id("sensor", sensor); - return this->try_send_entity_info_(static_cast(sensor), msg, - &APIConnection::send_list_entities_sensor_response); + fill_entity_info_base(sensor, msg); + return encode_message_to_buffer(msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } #endif #ifdef USE_SWITCH -bool APIConnection::send_switch_state(switch_::Switch *a_switch, bool state) { - return this->send_state_with_value_(a_switch, &APIConnection::try_send_switch_state_, - &APIConnection::try_send_switch_state_, state); +bool APIConnection::send_switch_state(switch_::Switch *a_switch) { + return this->schedule_message_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE); } void APIConnection::send_switch_info(switch_::Switch *a_switch) { - this->send_info_(static_cast(a_switch), - reinterpret_cast(&APIConnection::try_send_switch_info_)); + this->schedule_message_(a_switch, &APIConnection::try_send_switch_info, ListEntitiesSwitchResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_switch_state_(switch_::Switch *a_switch) { - return this->try_send_switch_state_(a_switch, a_switch->state); -} -bool APIConnection::try_send_switch_state_(switch_::Switch *a_switch, bool state) { - SwitchStateResponse resp; - resp.state = state; - resp.key = a_switch->get_object_id_hash(); - return this->send_switch_state_response(resp); +uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *a_switch = static_cast(entity); + SwitchStateResponse resp; + resp.state = a_switch->state; + fill_entity_state_base(a_switch, resp); + return encode_message_to_buffer(resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_switch_info_(switch_::Switch *a_switch) { + +uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *a_switch = static_cast(entity); ListEntitiesSwitchResponse msg; msg.assumed_state = a_switch->assumed_state(); msg.device_class = a_switch->get_device_class(); msg.unique_id = get_default_unique_id("switch", a_switch); - return this->try_send_entity_info_(static_cast(a_switch), msg, - &APIConnection::send_list_entities_switch_response); + fill_entity_info_base(a_switch, msg); + return encode_message_to_buffer(msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::switch_command(const SwitchCommandRequest &msg) { switch_::Switch *a_switch = App.get_switch_by_key(msg.key); @@ -595,48 +614,46 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) { #endif #ifdef USE_TEXT_SENSOR -bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state) { - return this->send_state_with_value_(text_sensor, &APIConnection::try_send_text_sensor_state_, - &APIConnection::try_send_text_sensor_state_, std::move(state)); +bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor) { + return this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_state, + TextSensorStateResponse::MESSAGE_TYPE); } void APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) { - this->send_info_(static_cast(text_sensor), - reinterpret_cast(&APIConnection::try_send_text_sensor_info_)); + this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_info, + ListEntitiesTextSensorResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor) { - return this->try_send_text_sensor_state_(text_sensor, text_sensor->state); -} -bool APIConnection::try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor, std::string state) { - TextSensorStateResponse resp; - resp.state = std::move(state); - resp.missing_state = !text_sensor->has_state(); - resp.key = text_sensor->get_object_id_hash(); - return this->send_text_sensor_state_response(resp); +uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *text_sensor = static_cast(entity); + TextSensorStateResponse resp; + resp.state = text_sensor->state; + resp.missing_state = !text_sensor->has_state(); + fill_entity_state_base(text_sensor, resp); + return encode_message_to_buffer(resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_text_sensor_info_(text_sensor::TextSensor *text_sensor) { +uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *text_sensor = static_cast(entity); ListEntitiesTextSensorResponse msg; msg.device_class = text_sensor->get_device_class(); msg.unique_id = text_sensor->unique_id(); if (msg.unique_id.empty()) msg.unique_id = get_default_unique_id("text_sensor", text_sensor); - return this->try_send_entity_info_(static_cast(text_sensor), msg, - &APIConnection::send_list_entities_text_sensor_response); + fill_entity_info_base(text_sensor, msg); + return encode_message_to_buffer(msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } #endif #ifdef USE_CLIMATE bool APIConnection::send_climate_state(climate::Climate *climate) { - return this->send_state_(static_cast(climate), - reinterpret_cast(&APIConnection::try_send_climate_state_)); + return this->schedule_message_(climate, &APIConnection::try_send_climate_state, ClimateStateResponse::MESSAGE_TYPE); } -void APIConnection::send_climate_info(climate::Climate *climate) { - this->send_info_(static_cast(climate), - reinterpret_cast(&APIConnection::try_send_climate_info_)); -} -bool APIConnection::try_send_climate_state_(climate::Climate *climate) { +uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *climate = static_cast(entity); ClimateStateResponse resp; - resp.key = climate->get_object_id_hash(); + fill_entity_state_base(climate, resp); auto traits = climate->get_traits(); resp.mode = static_cast(climate->mode); resp.action = static_cast(climate->action); @@ -663,9 +680,14 @@ bool APIConnection::try_send_climate_state_(climate::Climate *climate) { resp.current_humidity = climate->current_humidity; if (traits.get_supports_target_humidity()) resp.target_humidity = climate->target_humidity; - return this->send_climate_state_response(resp); + return encode_message_to_buffer(resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_climate_info_(climate::Climate *climate) { +void APIConnection::send_climate_info(climate::Climate *climate) { + this->schedule_message_(climate, &APIConnection::try_send_climate_info, ListEntitiesClimateResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *climate = static_cast(entity); ListEntitiesClimateResponse msg; auto traits = climate->get_traits(); msg.supports_current_temperature = traits.get_supports_current_temperature(); @@ -693,8 +715,8 @@ bool APIConnection::try_send_climate_info_(climate::Climate *climate) { for (auto swing_mode : traits.get_supported_swing_modes()) msg.supported_swing_modes.push_back(static_cast(swing_mode)); msg.unique_id = get_default_unique_id("climate", climate); - return this->try_send_entity_info_(static_cast(climate), msg, - &APIConnection::send_list_entities_climate_response); + fill_entity_info_base(climate, msg); + return encode_message_to_buffer(msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::climate_command(const ClimateCommandRequest &msg) { climate::Climate *climate = App.get_climate_by_key(msg.key); @@ -727,26 +749,26 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) { #endif #ifdef USE_NUMBER -bool APIConnection::send_number_state(number::Number *number, float state) { - return this->send_state_with_value_(number, &APIConnection::try_send_number_state_, - &APIConnection::try_send_number_state_, state); +bool APIConnection::send_number_state(number::Number *number) { + return this->schedule_message_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE); } void APIConnection::send_number_info(number::Number *number) { - this->send_info_(static_cast(number), - reinterpret_cast(&APIConnection::try_send_number_info_)); + this->schedule_message_(number, &APIConnection::try_send_number_info, ListEntitiesNumberResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_number_state_(number::Number *number) { - return this->try_send_number_state_(number, number->state); -} -bool APIConnection::try_send_number_state_(number::Number *number, float state) { - NumberStateResponse resp; - resp.state = state; - resp.missing_state = !number->has_state(); - resp.key = number->get_object_id_hash(); - return this->send_number_state_response(resp); +uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *number = static_cast(entity); + NumberStateResponse resp; + resp.state = number->state; + resp.missing_state = !number->has_state(); + fill_entity_state_base(number, resp); + return encode_message_to_buffer(resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_number_info_(number::Number *number) { + +uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *number = static_cast(entity); ListEntitiesNumberResponse msg; msg.unit_of_measurement = number->traits.get_unit_of_measurement(); msg.mode = static_cast(number->traits.get_mode()); @@ -755,8 +777,8 @@ bool APIConnection::try_send_number_info_(number::Number *number) { msg.max_value = number->traits.get_max_value(); msg.step = number->traits.get_step(); msg.unique_id = get_default_unique_id("number", number); - return this->try_send_entity_info_(static_cast(number), msg, - &APIConnection::send_list_entities_number_response); + fill_entity_info_base(number, msg); + return encode_message_to_buffer(msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::number_command(const NumberCommandRequest &msg) { number::Number *number = App.get_number_by_key(msg.key); @@ -771,28 +793,29 @@ void APIConnection::number_command(const NumberCommandRequest &msg) { #ifdef USE_DATETIME_DATE bool APIConnection::send_date_state(datetime::DateEntity *date) { - return this->send_state_(static_cast(date), - reinterpret_cast(&APIConnection::try_send_date_state_)); + return this->schedule_message_(date, &APIConnection::try_send_date_state, DateStateResponse::MESSAGE_TYPE); } -void APIConnection::send_date_info(datetime::DateEntity *date) { - this->send_info_(static_cast(date), - reinterpret_cast(&APIConnection::try_send_date_info_)); -} -bool APIConnection::try_send_date_state_(datetime::DateEntity *date) { +uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *date = static_cast(entity); DateStateResponse resp; resp.missing_state = !date->has_state(); resp.year = date->year; resp.month = date->month; resp.day = date->day; - - resp.key = date->get_object_id_hash(); - return this->send_date_state_response(resp); + fill_entity_state_base(date, resp); + return encode_message_to_buffer(resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_date_info_(datetime::DateEntity *date) { +void APIConnection::send_date_info(datetime::DateEntity *date) { + this->schedule_message_(date, &APIConnection::try_send_date_info, ListEntitiesDateResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *date = static_cast(entity); ListEntitiesDateResponse msg; msg.unique_id = get_default_unique_id("date", date); - return this->try_send_entity_info_(static_cast(date), msg, - &APIConnection::send_list_entities_date_response); + fill_entity_info_base(date, msg); + return encode_message_to_buffer(msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::date_command(const DateCommandRequest &msg) { datetime::DateEntity *date = App.get_date_by_key(msg.key); @@ -807,28 +830,29 @@ void APIConnection::date_command(const DateCommandRequest &msg) { #ifdef USE_DATETIME_TIME bool APIConnection::send_time_state(datetime::TimeEntity *time) { - return this->send_state_(static_cast(time), - reinterpret_cast(&APIConnection::try_send_time_state_)); + return this->schedule_message_(time, &APIConnection::try_send_time_state, TimeStateResponse::MESSAGE_TYPE); } -void APIConnection::send_time_info(datetime::TimeEntity *time) { - this->send_info_(static_cast(time), - reinterpret_cast(&APIConnection::try_send_time_info_)); -} -bool APIConnection::try_send_time_state_(datetime::TimeEntity *time) { +uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *time = static_cast(entity); TimeStateResponse resp; resp.missing_state = !time->has_state(); resp.hour = time->hour; resp.minute = time->minute; resp.second = time->second; - - resp.key = time->get_object_id_hash(); - return this->send_time_state_response(resp); + fill_entity_state_base(time, resp); + return encode_message_to_buffer(resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_time_info_(datetime::TimeEntity *time) { +void APIConnection::send_time_info(datetime::TimeEntity *time) { + this->schedule_message_(time, &APIConnection::try_send_time_info, ListEntitiesTimeResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *time = static_cast(entity); ListEntitiesTimeResponse msg; msg.unique_id = get_default_unique_id("time", time); - return this->try_send_entity_info_(static_cast(time), msg, - &APIConnection::send_list_entities_time_response); + fill_entity_info_base(time, msg); + return encode_message_to_buffer(msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::time_command(const TimeCommandRequest &msg) { datetime::TimeEntity *time = App.get_time_by_key(msg.key); @@ -843,29 +867,31 @@ void APIConnection::time_command(const TimeCommandRequest &msg) { #ifdef USE_DATETIME_DATETIME bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) { - return this->send_state_(static_cast(datetime), - reinterpret_cast(&APIConnection::try_send_datetime_state_)); + return this->schedule_message_(datetime, &APIConnection::try_send_datetime_state, + DateTimeStateResponse::MESSAGE_TYPE); } -void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) { - this->send_info_(static_cast(datetime), - reinterpret_cast(&APIConnection::try_send_datetime_info_)); -} -bool APIConnection::try_send_datetime_state_(datetime::DateTimeEntity *datetime) { +uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *datetime = static_cast(entity); DateTimeStateResponse resp; resp.missing_state = !datetime->has_state(); if (datetime->has_state()) { ESPTime state = datetime->state_as_esptime(); resp.epoch_seconds = state.timestamp; } - - resp.key = datetime->get_object_id_hash(); - return this->send_date_time_state_response(resp); + fill_entity_state_base(datetime, resp); + return encode_message_to_buffer(resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_datetime_info_(datetime::DateTimeEntity *datetime) { +void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) { + this->schedule_message_(datetime, &APIConnection::try_send_datetime_info, ListEntitiesDateTimeResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *datetime = static_cast(entity); ListEntitiesDateTimeResponse msg; msg.unique_id = get_default_unique_id("datetime", datetime); - return this->try_send_entity_info_(static_cast(datetime), msg, - &APIConnection::send_list_entities_date_time_response); + fill_entity_info_base(datetime, msg); + return encode_message_to_buffer(msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::datetime_command(const DateTimeCommandRequest &msg) { datetime::DateTimeEntity *datetime = App.get_datetime_by_key(msg.key); @@ -879,32 +905,34 @@ void APIConnection::datetime_command(const DateTimeCommandRequest &msg) { #endif #ifdef USE_TEXT -bool APIConnection::send_text_state(text::Text *text, std::string state) { - return this->send_state_with_value_(text, &APIConnection::try_send_text_state_, &APIConnection::try_send_text_state_, - std::move(state)); +bool APIConnection::send_text_state(text::Text *text) { + return this->schedule_message_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE); } void APIConnection::send_text_info(text::Text *text) { - this->send_info_(static_cast(text), - reinterpret_cast(&APIConnection::try_send_text_info_)); + this->schedule_message_(text, &APIConnection::try_send_text_info, ListEntitiesTextResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_text_state_(text::Text *text) { return this->try_send_text_state_(text, text->state); } -bool APIConnection::try_send_text_state_(text::Text *text, std::string state) { - TextStateResponse resp; - resp.state = std::move(state); - resp.missing_state = !text->has_state(); - resp.key = text->get_object_id_hash(); - return this->send_text_state_response(resp); +uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *text = static_cast(entity); + TextStateResponse resp; + resp.state = text->state; + resp.missing_state = !text->has_state(); + fill_entity_state_base(text, resp); + return encode_message_to_buffer(resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_text_info_(text::Text *text) { + +uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *text = static_cast(entity); ListEntitiesTextResponse msg; msg.mode = static_cast(text->traits.get_mode()); msg.min_length = text->traits.get_min_length(); msg.max_length = text->traits.get_max_length(); msg.pattern = text->traits.get_pattern(); msg.unique_id = get_default_unique_id("text", text); - return this->try_send_entity_info_(static_cast(text), msg, - &APIConnection::send_list_entities_text_response); + fill_entity_info_base(text, msg); + return encode_message_to_buffer(msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::text_command(const TextCommandRequest &msg) { text::Text *text = App.get_text_by_key(msg.key); @@ -918,32 +946,32 @@ void APIConnection::text_command(const TextCommandRequest &msg) { #endif #ifdef USE_SELECT -bool APIConnection::send_select_state(select::Select *select, std::string state) { - return this->send_state_with_value_(select, &APIConnection::try_send_select_state_, - &APIConnection::try_send_select_state_, std::move(state)); +bool APIConnection::send_select_state(select::Select *select) { + return this->schedule_message_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE); } void APIConnection::send_select_info(select::Select *select) { - this->send_info_(static_cast(select), - reinterpret_cast(&APIConnection::try_send_select_info_)); + this->schedule_message_(select, &APIConnection::try_send_select_info, ListEntitiesSelectResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_select_state_(select::Select *select) { - return this->try_send_select_state_(select, select->state); -} -bool APIConnection::try_send_select_state_(select::Select *select, std::string state) { - SelectStateResponse resp; - resp.state = std::move(state); - resp.missing_state = !select->has_state(); - resp.key = select->get_object_id_hash(); - return this->send_select_state_response(resp); +uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *select = static_cast(entity); + SelectStateResponse resp; + resp.state = select->state; + resp.missing_state = !select->has_state(); + fill_entity_state_base(select, resp); + return encode_message_to_buffer(resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_select_info_(select::Select *select) { + +uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *select = static_cast(entity); ListEntitiesSelectResponse msg; for (const auto &option : select->traits.get_options()) msg.options.push_back(option); msg.unique_id = get_default_unique_id("select", select); - return this->try_send_entity_info_(static_cast(select), msg, - &APIConnection::send_list_entities_select_response); + fill_entity_info_base(select, msg); + return encode_message_to_buffer(msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::select_command(const SelectCommandRequest &msg) { select::Select *select = App.get_select_by_key(msg.key); @@ -958,15 +986,16 @@ void APIConnection::select_command(const SelectCommandRequest &msg) { #ifdef USE_BUTTON void esphome::api::APIConnection::send_button_info(button::Button *button) { - this->send_info_(static_cast(button), - reinterpret_cast(&APIConnection::try_send_button_info_)); + this->schedule_message_(button, &APIConnection::try_send_button_info, ListEntitiesButtonResponse::MESSAGE_TYPE); } -bool esphome::api::APIConnection::try_send_button_info_(button::Button *button) { +uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *button = static_cast(entity); ListEntitiesButtonResponse msg; msg.device_class = button->get_device_class(); msg.unique_id = get_default_unique_id("button", button); - return this->try_send_entity_info_(static_cast(button), msg, - &APIConnection::send_list_entities_button_response); + fill_entity_info_base(button, msg); + return encode_message_to_buffer(msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg) { button::Button *button = App.get_button_by_key(msg.key); @@ -978,32 +1007,32 @@ void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg #endif #ifdef USE_LOCK -bool APIConnection::send_lock_state(lock::Lock *a_lock, lock::LockState state) { - return this->send_state_with_value_(a_lock, &APIConnection::try_send_lock_state_, - &APIConnection::try_send_lock_state_, state); +bool APIConnection::send_lock_state(lock::Lock *a_lock) { + return this->schedule_message_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE); } void APIConnection::send_lock_info(lock::Lock *a_lock) { - this->send_info_(static_cast(a_lock), - reinterpret_cast(&APIConnection::try_send_lock_info_)); + this->schedule_message_(a_lock, &APIConnection::try_send_lock_info, ListEntitiesLockResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_lock_state_(lock::Lock *a_lock) { - return this->try_send_lock_state_(a_lock, a_lock->state); -} -bool APIConnection::try_send_lock_state_(lock::Lock *a_lock, lock::LockState state) { - LockStateResponse resp; - resp.state = static_cast(state); - resp.key = a_lock->get_object_id_hash(); - return this->send_lock_state_response(resp); +uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *a_lock = static_cast(entity); + LockStateResponse resp; + resp.state = static_cast(a_lock->state); + fill_entity_state_base(a_lock, resp); + return encode_message_to_buffer(resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_lock_info_(lock::Lock *a_lock) { + +uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *a_lock = static_cast(entity); ListEntitiesLockResponse msg; msg.assumed_state = a_lock->traits.get_assumed_state(); msg.supports_open = a_lock->traits.get_supports_open(); msg.requires_code = a_lock->traits.get_requires_code(); msg.unique_id = get_default_unique_id("lock", a_lock); - return this->try_send_entity_info_(static_cast(a_lock), msg, - &APIConnection::send_list_entities_lock_response); + fill_entity_info_base(a_lock, msg); + return encode_message_to_buffer(msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::lock_command(const LockCommandRequest &msg) { lock::Lock *a_lock = App.get_lock_by_key(msg.key); @@ -1026,22 +1055,23 @@ void APIConnection::lock_command(const LockCommandRequest &msg) { #ifdef USE_VALVE bool APIConnection::send_valve_state(valve::Valve *valve) { - return this->send_state_(static_cast(valve), - reinterpret_cast(&APIConnection::try_send_valve_state_)); + return this->schedule_message_(valve, &APIConnection::try_send_valve_state, ValveStateResponse::MESSAGE_TYPE); } -void APIConnection::send_valve_info(valve::Valve *valve) { - this->send_info_(static_cast(valve), - reinterpret_cast(&APIConnection::try_send_valve_info_)); -} -bool APIConnection::try_send_valve_state_(valve::Valve *valve) { +uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *valve = static_cast(entity); ValveStateResponse resp; resp.position = valve->position; resp.current_operation = static_cast(valve->current_operation); - - resp.key = valve->get_object_id_hash(); - return this->send_valve_state_response(resp); + fill_entity_state_base(valve, resp); + return encode_message_to_buffer(resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_valve_info_(valve::Valve *valve) { +void APIConnection::send_valve_info(valve::Valve *valve) { + this->schedule_message_(valve, &APIConnection::try_send_valve_info, ListEntitiesValveResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *valve = static_cast(entity); ListEntitiesValveResponse msg; auto traits = valve->get_traits(); msg.device_class = valve->get_device_class(); @@ -1049,8 +1079,8 @@ bool APIConnection::try_send_valve_info_(valve::Valve *valve) { msg.supports_position = traits.get_supports_position(); msg.supports_stop = traits.get_supports_stop(); msg.unique_id = get_default_unique_id("valve", valve); - return this->try_send_entity_info_(static_cast(valve), msg, - &APIConnection::send_list_entities_valve_response); + fill_entity_info_base(valve, msg); + return encode_message_to_buffer(msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::valve_command(const ValveCommandRequest &msg) { valve::Valve *valve = App.get_valve_by_key(msg.key); @@ -1068,14 +1098,12 @@ void APIConnection::valve_command(const ValveCommandRequest &msg) { #ifdef USE_MEDIA_PLAYER bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) { - return this->send_state_(static_cast(media_player), - reinterpret_cast(&APIConnection::try_send_media_player_state_)); + return this->schedule_message_(media_player, &APIConnection::try_send_media_player_state, + MediaPlayerStateResponse::MESSAGE_TYPE); } -void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) { - this->send_info_(static_cast(media_player), - reinterpret_cast(&APIConnection::try_send_media_player_info_)); -} -bool APIConnection::try_send_media_player_state_(media_player::MediaPlayer *media_player) { +uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *media_player = static_cast(entity); MediaPlayerStateResponse resp; media_player::MediaPlayerState report_state = media_player->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING ? media_player::MEDIA_PLAYER_STATE_PLAYING @@ -1083,11 +1111,16 @@ bool APIConnection::try_send_media_player_state_(media_player::MediaPlayer *medi resp.state = static_cast(report_state); resp.volume = media_player->volume; resp.muted = media_player->is_muted(); - - resp.key = media_player->get_object_id_hash(); - return this->send_media_player_state_response(resp); + fill_entity_state_base(media_player, resp); + return encode_message_to_buffer(resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_media_player_info_(media_player::MediaPlayer *media_player) { +void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) { + this->schedule_message_(media_player, &APIConnection::try_send_media_player_info, + ListEntitiesMediaPlayerResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *media_player = static_cast(entity); ListEntitiesMediaPlayerResponse msg; auto traits = media_player->get_traits(); msg.supports_pause = traits.get_supports_pause(); @@ -1101,8 +1134,8 @@ bool APIConnection::try_send_media_player_info_(media_player::MediaPlayer *media msg.supported_formats.push_back(media_format); } msg.unique_id = get_default_unique_id("media_player", media_player); - return this->try_send_entity_info_(static_cast(media_player), msg, - &APIConnection::send_list_entities_media_player_response); + fill_entity_info_base(media_player, msg); + return encode_message_to_buffer(msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) { media_player::MediaPlayer *media_player = App.get_media_player_by_key(msg.key); @@ -1137,14 +1170,15 @@ void APIConnection::set_camera_state(std::shared_ptr this->image_reader_.set_image(std::move(image)); } void APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) { - this->send_info_(static_cast(camera), - reinterpret_cast(&APIConnection::try_send_camera_info_)); + this->schedule_message_(camera, &APIConnection::try_send_camera_info, ListEntitiesCameraResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_camera_info_(esp32_camera::ESP32Camera *camera) { +uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *camera = static_cast(entity); ListEntitiesCameraResponse msg; msg.unique_id = get_default_unique_id("camera", camera); - return this->try_send_entity_info_(static_cast(camera), msg, - &APIConnection::send_list_entities_camera_response); + fill_entity_info_base(camera, msg); + return encode_message_to_buffer(msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::camera_image(const CameraImageRequest &msg) { if (esp32_camera::global_esp32_camera == nullptr) @@ -1187,9 +1221,9 @@ bool APIConnection::send_bluetooth_le_advertisement(const BluetoothLEAdvertiseme manufacturer_data.legacy_data.assign(manufacturer_data.data.begin(), manufacturer_data.data.end()); manufacturer_data.data.clear(); } - return this->send_bluetooth_le_advertisement_response(resp); + return this->send_message(resp); } - return this->send_bluetooth_le_advertisement_response(msg); + return this->send_message(msg); } void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) { bluetooth_proxy::global_bluetooth_proxy->bluetooth_device_request(msg); @@ -1333,28 +1367,32 @@ void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetCon #ifdef USE_ALARM_CONTROL_PANEL bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { - return this->send_state_(static_cast(a_alarm_control_panel), - reinterpret_cast(&APIConnection::try_send_alarm_control_panel_state_)); + return this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_state, + AlarmControlPanelStateResponse::MESSAGE_TYPE); } -void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { - this->send_info_(static_cast(a_alarm_control_panel), - reinterpret_cast(&APIConnection::try_send_alarm_control_panel_info_)); -} -bool APIConnection::try_send_alarm_control_panel_state_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { +uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn, + uint32_t remaining_size, bool is_single) { + auto *a_alarm_control_panel = static_cast(entity); AlarmControlPanelStateResponse resp; resp.state = static_cast(a_alarm_control_panel->get_state()); - - resp.key = a_alarm_control_panel->get_object_id_hash(); - return this->send_alarm_control_panel_state_response(resp); + fill_entity_state_base(a_alarm_control_panel, resp); + return encode_message_to_buffer(resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_alarm_control_panel_info_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { +void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { + this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_info, + ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn, + uint32_t remaining_size, bool is_single) { + auto *a_alarm_control_panel = static_cast(entity); ListEntitiesAlarmControlPanelResponse msg; msg.supported_features = a_alarm_control_panel->get_supported_features(); msg.requires_code = a_alarm_control_panel->get_requires_code(); msg.requires_code_to_arm = a_alarm_control_panel->get_requires_code_to_arm(); msg.unique_id = get_default_unique_id("alarm_control_panel", a_alarm_control_panel); - return this->try_send_entity_info_(static_cast(a_alarm_control_panel), msg, - &APIConnection::send_list_entities_alarm_control_panel_response); + fill_entity_info_base(a_alarm_control_panel, msg); + return encode_message_to_buffer(msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE, conn, remaining_size, + is_single); } void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) { alarm_control_panel::AlarmControlPanel *a_alarm_control_panel = App.get_alarm_control_panel_by_key(msg.key); @@ -1391,45 +1429,40 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe #endif #ifdef USE_EVENT -void APIConnection::send_event(event::Event *event, std::string event_type) { - this->send_state_with_value_(event, &APIConnection::try_send_event_, &APIConnection::try_send_event_, - std::move(event_type)); +void APIConnection::send_event(event::Event *event, const std::string &event_type) { + this->schedule_message_(event, MessageCreator(event_type, EventResponse::MESSAGE_TYPE), EventResponse::MESSAGE_TYPE); } void APIConnection::send_event_info(event::Event *event) { - this->send_info_(static_cast(event), - reinterpret_cast(&APIConnection::try_send_event_info_)); + this->schedule_message_(event, &APIConnection::try_send_event_info, ListEntitiesEventResponse::MESSAGE_TYPE); } -bool APIConnection::try_send_event_(event::Event *event) { - return this->try_send_event_(event, *(event->last_event_type)); -} -bool APIConnection::try_send_event_(event::Event *event, std::string event_type) { +uint16_t APIConnection::try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn, + uint32_t remaining_size, bool is_single) { EventResponse resp; - resp.event_type = std::move(event_type); - - resp.key = event->get_object_id_hash(); - return this->send_event_response(resp); + resp.event_type = event_type; + fill_entity_state_base(event, resp); + return encode_message_to_buffer(resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_event_info_(event::Event *event) { + +uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *event = static_cast(entity); ListEntitiesEventResponse msg; msg.device_class = event->get_device_class(); for (const auto &event_type : event->get_event_types()) msg.event_types.push_back(event_type); msg.unique_id = get_default_unique_id("event", event); - return this->try_send_entity_info_(static_cast(event), msg, - &APIConnection::send_list_entities_event_response); + fill_entity_info_base(event, msg); + return encode_message_to_buffer(msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } #endif #ifdef USE_UPDATE bool APIConnection::send_update_state(update::UpdateEntity *update) { - return this->send_state_(static_cast(update), - reinterpret_cast(&APIConnection::try_send_update_state_)); + return this->schedule_message_(update, &APIConnection::try_send_update_state, UpdateStateResponse::MESSAGE_TYPE); } -void APIConnection::send_update_info(update::UpdateEntity *update) { - this->send_info_(static_cast(update), - reinterpret_cast(&APIConnection::try_send_update_info_)); -} -bool APIConnection::try_send_update_state_(update::UpdateEntity *update) { +uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *update = static_cast(entity); UpdateStateResponse resp; resp.missing_state = !update->has_state(); if (update->has_state()) { @@ -1444,16 +1477,20 @@ bool APIConnection::try_send_update_state_(update::UpdateEntity *update) { resp.release_summary = update->update_info.summary; resp.release_url = update->update_info.release_url; } - - resp.key = update->get_object_id_hash(); - return this->send_update_state_response(resp); + fill_entity_state_base(update, resp); + return encode_message_to_buffer(resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } -bool APIConnection::try_send_update_info_(update::UpdateEntity *update) { +void APIConnection::send_update_info(update::UpdateEntity *update) { + this->schedule_message_(update, &APIConnection::try_send_update_info, ListEntitiesUpdateResponse::MESSAGE_TYPE); +} +uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + auto *update = static_cast(entity); ListEntitiesUpdateResponse msg; msg.device_class = update->get_device_class(); msg.unique_id = get_default_unique_id("update", update); - return this->try_send_entity_info_(static_cast(update), msg, - &APIConnection::send_list_entities_update_response); + fill_entity_info_base(update, msg); + return encode_message_to_buffer(msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } void APIConnection::update_command(const UpdateCommandRequest &msg) { update::UpdateEntity *update = App.get_update_by_key(msg.key); @@ -1468,7 +1505,7 @@ void APIConnection::update_command(const UpdateCommandRequest &msg) { update->check(); break; case enums::UPDATE_COMMAND_NONE: - ESP_LOGE(TAG, "UPDATE_COMMAND_NONE not handled. Check client is sending the correct command"); + ESP_LOGE(TAG, "UPDATE_COMMAND_NONE not handled; confirm command is correct"); break; default: ESP_LOGW(TAG, "Unknown update command: %" PRIu32, msg.command); @@ -1501,7 +1538,7 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char buffer.encode_string(3, line, line_length); // string message = 3 // SubscribeLogsResponse - 29 - return this->send_buffer(buffer, 29); + return this->send_buffer(buffer, SubscribeLogsResponse::MESSAGE_TYPE); } HelloResponse APIConnection::hello(const HelloRequest &msg) { @@ -1530,7 +1567,7 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) { // bool invalid_password = 1; resp.invalid_password = !correct; if (correct) { - ESP_LOGD(TAG, "%s: Connected successfully", this->client_combined_info_.c_str()); + ESP_LOGD(TAG, "%s connected", this->client_combined_info_.c_str()); this->connection_state_ = ConnectionState::AUTHENTICATED; this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_); #ifdef USE_HOMEASSISTANT_TIME @@ -1601,7 +1638,7 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) { } } if (!found) { - ESP_LOGV(TAG, "Could not find matching service!"); + ESP_LOGV(TAG, "Could not find service"); } } #ifdef USE_API_NOISE @@ -1647,8 +1684,8 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) { } return false; } -bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) { - if (!this->try_to_clear_buffer(message_type != 29)) { // SubscribeLogsResponse +bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) { + if (!this->try_to_clear_buffer(message_type != SubscribeLogsResponse::MESSAGE_TYPE)) { // SubscribeLogsResponse return false; } @@ -1670,17 +1707,354 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) } void APIConnection::on_unauthenticated_access() { this->on_fatal_error(); - ESP_LOGD(TAG, "%s: tried to access without authentication.", this->client_combined_info_.c_str()); + ESP_LOGD(TAG, "%s requested access without authentication", this->client_combined_info_.c_str()); } void APIConnection::on_no_setup_connection() { this->on_fatal_error(); - ESP_LOGD(TAG, "%s: tried to access without full connection.", this->client_combined_info_.c_str()); + ESP_LOGD(TAG, "%s requested access without full connection", this->client_combined_info_.c_str()); } void APIConnection::on_fatal_error() { this->helper_->close(); this->remove_ = true; } +void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type) { + // Check if we already have a message of this type for this entity + // This provides deduplication per entity/message_type combination + // O(n) but optimized for RAM and not performance. + for (auto &item : items) { + if (item.entity == entity && item.message_type == message_type) { + // Update the existing item with the new creator + item.creator = std::move(creator); + return; + } + } + + // No existing item found, add new one + items.emplace_back(entity, std::move(creator), message_type); +} + +bool APIConnection::schedule_batch_() { + if (!this->deferred_batch_.batch_scheduled) { + this->deferred_batch_.batch_scheduled = true; + this->deferred_batch_.batch_start_time = App.get_loop_component_start_time(); + } + return true; +} + +ProtoWriteBuffer APIConnection::allocate_single_message_buffer(uint16_t size) { return this->create_buffer(size); } + +ProtoWriteBuffer APIConnection::allocate_batch_message_buffer(uint16_t size) { + ProtoWriteBuffer result = this->prepare_message_buffer(size, this->batch_first_message_); + this->batch_first_message_ = false; + return result; +} + +void APIConnection::process_batch_() { + if (this->deferred_batch_.empty()) { + this->deferred_batch_.batch_scheduled = false; + return; + } + + // Try to clear buffer first + if (!this->try_to_clear_buffer(true)) { + // Can't write now, we'll try again later + return; + } + + size_t num_items = this->deferred_batch_.items.size(); + + // Fast path for single message - allocate exact size needed + if (num_items == 1) { + const auto &item = this->deferred_batch_.items[0]; + + // Let the creator calculate size and encode if it fits + uint16_t payload_size = item.creator(item.entity, this, std::numeric_limits::max(), true); + + if (payload_size > 0 && + this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, item.message_type)) { + this->deferred_batch_.clear(); + } else if (payload_size == 0) { + // Message too large + ESP_LOGW(TAG, "Message too large to send: type=%u", item.message_type); + this->deferred_batch_.clear(); + } + return; + } + + // Pre-allocate storage for packet info + std::vector packet_info; + packet_info.reserve(num_items); + + // Cache these values to avoid repeated virtual calls + const uint8_t header_padding = this->helper_->frame_header_padding(); + const uint8_t footer_size = this->helper_->frame_footer_size(); + + // Initialize buffer and tracking variables + this->parent_->get_shared_buffer_ref().clear(); + + // Pre-calculate exact buffer size needed based on message types + uint32_t total_estimated_size = 0; + for (const auto &item : this->deferred_batch_.items) { + total_estimated_size += get_estimated_message_size(item.message_type); + } + + // Calculate total overhead for all messages + uint32_t total_overhead = (header_padding + footer_size) * num_items; + + // Reserve based on estimated size (much more accurate than 24-byte worst-case) + this->parent_->get_shared_buffer_ref().reserve(total_estimated_size + total_overhead); + this->batch_first_message_ = true; + + size_t items_processed = 0; + uint16_t remaining_size = std::numeric_limits::max(); + + // Track where each message's header padding begins in the buffer + // For plaintext: this is where the 6-byte header padding starts + // For noise: this is where the 7-byte header padding starts + // The actual message data follows after the header padding + uint32_t current_offset = 0; + + // Process items and encode directly to buffer + for (const auto &item : this->deferred_batch_.items) { + // Try to encode message + // The creator will calculate overhead to determine if the message fits + uint16_t payload_size = item.creator(item.entity, this, remaining_size, false); + + if (payload_size == 0) { + // Message won't fit, stop processing + break; + } + + // Message was encoded successfully + // payload_size is header_padding + actual payload size + footer_size + uint16_t proto_payload_size = payload_size - header_padding - footer_size; + packet_info.emplace_back(item.message_type, current_offset, proto_payload_size); + + // Update tracking variables + items_processed++; + // After first message, set remaining size to MAX_PACKET_SIZE to avoid fragmentation + if (items_processed == 1) { + remaining_size = MAX_PACKET_SIZE; + } + remaining_size -= payload_size; + // Calculate where the next message's header padding will start + // Current buffer size + footer space (that prepare_message_buffer will add for this message) + current_offset = this->parent_->get_shared_buffer_ref().size() + footer_size; + } + + if (items_processed == 0) { + this->deferred_batch_.clear(); + return; + } + + // Add footer space for the last message (for Noise protocol MAC) + if (footer_size > 0) { + auto &shared_buf = this->parent_->get_shared_buffer_ref(); + shared_buf.resize(shared_buf.size() + footer_size); + } + + // Send all collected packets + APIError err = + this->helper_->write_protobuf_packets(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, packet_info); + if (err != APIError::OK && err != APIError::WOULD_BLOCK) { + on_fatal_error(); + if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) { + ESP_LOGW(TAG, "%s: Connection reset during batch write", this->client_combined_info_.c_str()); + } else { + ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err), + errno); + } + } + + // Handle remaining items more efficiently + if (items_processed < this->deferred_batch_.items.size()) { + // Remove processed items from the beginning + this->deferred_batch_.items.erase(this->deferred_batch_.items.begin(), + this->deferred_batch_.items.begin() + items_processed); + + // Reschedule for remaining items + this->schedule_batch_(); + } else { + // All items processed + this->deferred_batch_.clear(); + } +} + +uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) const { + switch (message_type_) { + case 0: // Function pointer + return data_.ptr(entity, conn, remaining_size, is_single); + +#ifdef USE_EVENT + case EventResponse::MESSAGE_TYPE: { + auto *e = static_cast(entity); + return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single); + } +#endif + + default: + // Should not happen, return 0 to indicate no message + return 0; + } +} + +uint16_t APIConnection::try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + ListEntitiesDoneResponse resp; + return encode_message_to_buffer(resp, ListEntitiesDoneResponse::MESSAGE_TYPE, conn, remaining_size, is_single); +} + +uint16_t APIConnection::try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + DisconnectRequest req; + return encode_message_to_buffer(req, DisconnectRequest::MESSAGE_TYPE, conn, remaining_size, is_single); +} + +uint16_t APIConnection::get_estimated_message_size(uint16_t message_type) { + // Use generated ESTIMATED_SIZE constants from each message type + switch (message_type) { +#ifdef USE_BINARY_SENSOR + case BinarySensorStateResponse::MESSAGE_TYPE: + return BinarySensorStateResponse::ESTIMATED_SIZE; + case ListEntitiesBinarySensorResponse::MESSAGE_TYPE: + return ListEntitiesBinarySensorResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_SENSOR + case SensorStateResponse::MESSAGE_TYPE: + return SensorStateResponse::ESTIMATED_SIZE; + case ListEntitiesSensorResponse::MESSAGE_TYPE: + return ListEntitiesSensorResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_SWITCH + case SwitchStateResponse::MESSAGE_TYPE: + return SwitchStateResponse::ESTIMATED_SIZE; + case ListEntitiesSwitchResponse::MESSAGE_TYPE: + return ListEntitiesSwitchResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_TEXT_SENSOR + case TextSensorStateResponse::MESSAGE_TYPE: + return TextSensorStateResponse::ESTIMATED_SIZE; + case ListEntitiesTextSensorResponse::MESSAGE_TYPE: + return ListEntitiesTextSensorResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_NUMBER + case NumberStateResponse::MESSAGE_TYPE: + return NumberStateResponse::ESTIMATED_SIZE; + case ListEntitiesNumberResponse::MESSAGE_TYPE: + return ListEntitiesNumberResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_TEXT + case TextStateResponse::MESSAGE_TYPE: + return TextStateResponse::ESTIMATED_SIZE; + case ListEntitiesTextResponse::MESSAGE_TYPE: + return ListEntitiesTextResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_SELECT + case SelectStateResponse::MESSAGE_TYPE: + return SelectStateResponse::ESTIMATED_SIZE; + case ListEntitiesSelectResponse::MESSAGE_TYPE: + return ListEntitiesSelectResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_LOCK + case LockStateResponse::MESSAGE_TYPE: + return LockStateResponse::ESTIMATED_SIZE; + case ListEntitiesLockResponse::MESSAGE_TYPE: + return ListEntitiesLockResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_EVENT + case EventResponse::MESSAGE_TYPE: + return EventResponse::ESTIMATED_SIZE; + case ListEntitiesEventResponse::MESSAGE_TYPE: + return ListEntitiesEventResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_COVER + case CoverStateResponse::MESSAGE_TYPE: + return CoverStateResponse::ESTIMATED_SIZE; + case ListEntitiesCoverResponse::MESSAGE_TYPE: + return ListEntitiesCoverResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_FAN + case FanStateResponse::MESSAGE_TYPE: + return FanStateResponse::ESTIMATED_SIZE; + case ListEntitiesFanResponse::MESSAGE_TYPE: + return ListEntitiesFanResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_LIGHT + case LightStateResponse::MESSAGE_TYPE: + return LightStateResponse::ESTIMATED_SIZE; + case ListEntitiesLightResponse::MESSAGE_TYPE: + return ListEntitiesLightResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_CLIMATE + case ClimateStateResponse::MESSAGE_TYPE: + return ClimateStateResponse::ESTIMATED_SIZE; + case ListEntitiesClimateResponse::MESSAGE_TYPE: + return ListEntitiesClimateResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_ESP32_CAMERA + case ListEntitiesCameraResponse::MESSAGE_TYPE: + return ListEntitiesCameraResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_BUTTON + case ListEntitiesButtonResponse::MESSAGE_TYPE: + return ListEntitiesButtonResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_MEDIA_PLAYER + case MediaPlayerStateResponse::MESSAGE_TYPE: + return MediaPlayerStateResponse::ESTIMATED_SIZE; + case ListEntitiesMediaPlayerResponse::MESSAGE_TYPE: + return ListEntitiesMediaPlayerResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_ALARM_CONTROL_PANEL + case AlarmControlPanelStateResponse::MESSAGE_TYPE: + return AlarmControlPanelStateResponse::ESTIMATED_SIZE; + case ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE: + return ListEntitiesAlarmControlPanelResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_DATETIME_DATE + case DateStateResponse::MESSAGE_TYPE: + return DateStateResponse::ESTIMATED_SIZE; + case ListEntitiesDateResponse::MESSAGE_TYPE: + return ListEntitiesDateResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_DATETIME_TIME + case TimeStateResponse::MESSAGE_TYPE: + return TimeStateResponse::ESTIMATED_SIZE; + case ListEntitiesTimeResponse::MESSAGE_TYPE: + return ListEntitiesTimeResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_DATETIME_DATETIME + case DateTimeStateResponse::MESSAGE_TYPE: + return DateTimeStateResponse::ESTIMATED_SIZE; + case ListEntitiesDateTimeResponse::MESSAGE_TYPE: + return ListEntitiesDateTimeResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_VALVE + case ValveStateResponse::MESSAGE_TYPE: + return ValveStateResponse::ESTIMATED_SIZE; + case ListEntitiesValveResponse::MESSAGE_TYPE: + return ListEntitiesValveResponse::ESTIMATED_SIZE; +#endif +#ifdef USE_UPDATE + case UpdateStateResponse::MESSAGE_TYPE: + return UpdateStateResponse::ESTIMATED_SIZE; + case ListEntitiesUpdateResponse::MESSAGE_TYPE: + return ListEntitiesUpdateResponse::ESTIMATED_SIZE; +#endif + case ListEntitiesServicesResponse::MESSAGE_TYPE: + return ListEntitiesServicesResponse::ESTIMATED_SIZE; + case ListEntitiesDoneResponse::MESSAGE_TYPE: + return ListEntitiesDoneResponse::ESTIMATED_SIZE; + case DisconnectRequest::MESSAGE_TYPE: + return DisconnectRequest::ESTIMATED_SIZE; + default: + // Fallback for unknown message types + return 24; + } +} + } // namespace api } // namespace esphome #endif diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index f965a9e795..7cd41561d4 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -11,6 +11,7 @@ #include "esphome/core/entity_base.h" #include +#include namespace esphome { namespace api { @@ -18,49 +19,9 @@ namespace api { // Keepalive timeout in milliseconds static constexpr uint32_t KEEPALIVE_TIMEOUT_MS = 60000; -using send_message_t = bool (APIConnection::*)(void *); - -/* - This class holds a pointer to the source component that wants to publish a message, and a pointer to a function that - will lazily publish that message. The two pointers allow dedup in the deferred queue if multiple publishes for the - same component are backed up, and take up only 8 bytes of memory. The entry in the deferred queue (a std::vector) is - the DeferredMessage instance itself (not a pointer to one elsewhere in heap) so still only 8 bytes per entry. Even - 100 backed up messages (you'd have to have at least 100 sensors publishing because of dedup) would take up only 0.8 - kB. -*/ -class DeferredMessageQueue { - struct DeferredMessage { - friend class DeferredMessageQueue; - - protected: - void *source_; - send_message_t send_message_; - - public: - DeferredMessage(void *source, send_message_t send_message) : source_(source), send_message_(send_message) {} - bool operator==(const DeferredMessage &test) const { - return (source_ == test.source_ && send_message_ == test.send_message_); - } - } __attribute__((packed)); - - protected: - // vector is used very specifically for its zero memory overhead even though items are popped from the front (memory - // footprint is more important than speed here) - std::vector deferred_queue_; - APIConnection *api_connection_; - - // helper for allowing only unique entries in the queue - void dmq_push_back_with_dedup_(void *source, send_message_t send_message); - - public: - DeferredMessageQueue(APIConnection *api_connection) : api_connection_(api_connection) {} - void process_queue(); - void defer(void *source, send_message_t send_message); - bool empty() const { return deferred_queue_.empty(); } -}; - class APIConnection : public APIServerConnection { public: + friend class APIServer; APIConnection(std::unique_ptr socket, APIServer *parent); virtual ~APIConnection(); @@ -68,225 +29,105 @@ class APIConnection : public APIServerConnection { void loop(); bool send_list_info_done() { - ListEntitiesDoneResponse resp; - return this->send_list_entities_done_response(resp); + return this->schedule_message_(nullptr, &APIConnection::try_send_list_info_done, + ListEntitiesDoneResponse::MESSAGE_TYPE); } #ifdef USE_BINARY_SENSOR - bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state); + bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor); void send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor); - - protected: - bool try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor); - bool try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor, bool state); - bool try_send_binary_sensor_info_(binary_sensor::BinarySensor *binary_sensor); - - public: #endif #ifdef USE_COVER bool send_cover_state(cover::Cover *cover); void send_cover_info(cover::Cover *cover); void cover_command(const CoverCommandRequest &msg) override; - - protected: - bool try_send_cover_state_(cover::Cover *cover); - bool try_send_cover_info_(cover::Cover *cover); - - public: #endif #ifdef USE_FAN bool send_fan_state(fan::Fan *fan); void send_fan_info(fan::Fan *fan); void fan_command(const FanCommandRequest &msg) override; - - protected: - bool try_send_fan_state_(fan::Fan *fan); - bool try_send_fan_info_(fan::Fan *fan); - - public: #endif #ifdef USE_LIGHT bool send_light_state(light::LightState *light); void send_light_info(light::LightState *light); void light_command(const LightCommandRequest &msg) override; - - protected: - bool try_send_light_state_(light::LightState *light); - bool try_send_light_info_(light::LightState *light); - - public: #endif #ifdef USE_SENSOR - bool send_sensor_state(sensor::Sensor *sensor, float state); + bool send_sensor_state(sensor::Sensor *sensor); void send_sensor_info(sensor::Sensor *sensor); - - protected: - bool try_send_sensor_state_(sensor::Sensor *sensor); - bool try_send_sensor_state_(sensor::Sensor *sensor, float state); - bool try_send_sensor_info_(sensor::Sensor *sensor); - - public: #endif #ifdef USE_SWITCH - bool send_switch_state(switch_::Switch *a_switch, bool state); + bool send_switch_state(switch_::Switch *a_switch); void send_switch_info(switch_::Switch *a_switch); void switch_command(const SwitchCommandRequest &msg) override; - - protected: - bool try_send_switch_state_(switch_::Switch *a_switch); - bool try_send_switch_state_(switch_::Switch *a_switch, bool state); - bool try_send_switch_info_(switch_::Switch *a_switch); - - public: #endif #ifdef USE_TEXT_SENSOR - bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state); + bool send_text_sensor_state(text_sensor::TextSensor *text_sensor); void send_text_sensor_info(text_sensor::TextSensor *text_sensor); - - protected: - bool try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor); - bool try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor, std::string state); - bool try_send_text_sensor_info_(text_sensor::TextSensor *text_sensor); - - public: #endif #ifdef USE_ESP32_CAMERA void set_camera_state(std::shared_ptr image); void send_camera_info(esp32_camera::ESP32Camera *camera); void camera_image(const CameraImageRequest &msg) override; - - protected: - bool try_send_camera_info_(esp32_camera::ESP32Camera *camera); - - public: #endif #ifdef USE_CLIMATE bool send_climate_state(climate::Climate *climate); void send_climate_info(climate::Climate *climate); void climate_command(const ClimateCommandRequest &msg) override; - - protected: - bool try_send_climate_state_(climate::Climate *climate); - bool try_send_climate_info_(climate::Climate *climate); - - public: #endif #ifdef USE_NUMBER - bool send_number_state(number::Number *number, float state); + bool send_number_state(number::Number *number); void send_number_info(number::Number *number); void number_command(const NumberCommandRequest &msg) override; - - protected: - bool try_send_number_state_(number::Number *number); - bool try_send_number_state_(number::Number *number, float state); - bool try_send_number_info_(number::Number *number); - - public: #endif #ifdef USE_DATETIME_DATE bool send_date_state(datetime::DateEntity *date); void send_date_info(datetime::DateEntity *date); void date_command(const DateCommandRequest &msg) override; - - protected: - bool try_send_date_state_(datetime::DateEntity *date); - bool try_send_date_info_(datetime::DateEntity *date); - - public: #endif #ifdef USE_DATETIME_TIME bool send_time_state(datetime::TimeEntity *time); void send_time_info(datetime::TimeEntity *time); void time_command(const TimeCommandRequest &msg) override; - - protected: - bool try_send_time_state_(datetime::TimeEntity *time); - bool try_send_time_info_(datetime::TimeEntity *time); - - public: #endif #ifdef USE_DATETIME_DATETIME bool send_datetime_state(datetime::DateTimeEntity *datetime); void send_datetime_info(datetime::DateTimeEntity *datetime); void datetime_command(const DateTimeCommandRequest &msg) override; - - protected: - bool try_send_datetime_state_(datetime::DateTimeEntity *datetime); - bool try_send_datetime_info_(datetime::DateTimeEntity *datetime); - - public: #endif #ifdef USE_TEXT - bool send_text_state(text::Text *text, std::string state); + bool send_text_state(text::Text *text); void send_text_info(text::Text *text); void text_command(const TextCommandRequest &msg) override; - - protected: - bool try_send_text_state_(text::Text *text); - bool try_send_text_state_(text::Text *text, std::string state); - bool try_send_text_info_(text::Text *text); - - public: #endif #ifdef USE_SELECT - bool send_select_state(select::Select *select, std::string state); + bool send_select_state(select::Select *select); void send_select_info(select::Select *select); void select_command(const SelectCommandRequest &msg) override; - - protected: - bool try_send_select_state_(select::Select *select); - bool try_send_select_state_(select::Select *select, std::string state); - bool try_send_select_info_(select::Select *select); - - public: #endif #ifdef USE_BUTTON void send_button_info(button::Button *button); void button_command(const ButtonCommandRequest &msg) override; - - protected: - bool try_send_button_info_(button::Button *button); - - public: #endif #ifdef USE_LOCK - bool send_lock_state(lock::Lock *a_lock, lock::LockState state); + bool send_lock_state(lock::Lock *a_lock); void send_lock_info(lock::Lock *a_lock); void lock_command(const LockCommandRequest &msg) override; - - protected: - bool try_send_lock_state_(lock::Lock *a_lock); - bool try_send_lock_state_(lock::Lock *a_lock, lock::LockState state); - bool try_send_lock_info_(lock::Lock *a_lock); - - public: #endif #ifdef USE_VALVE bool send_valve_state(valve::Valve *valve); void send_valve_info(valve::Valve *valve); void valve_command(const ValveCommandRequest &msg) override; - - protected: - bool try_send_valve_state_(valve::Valve *valve); - bool try_send_valve_info_(valve::Valve *valve); - - public: #endif #ifdef USE_MEDIA_PLAYER bool send_media_player_state(media_player::MediaPlayer *media_player); void send_media_player_info(media_player::MediaPlayer *media_player); void media_player_command(const MediaPlayerCommandRequest &msg) override; - - protected: - bool try_send_media_player_state_(media_player::MediaPlayer *media_player); - bool try_send_media_player_info_(media_player::MediaPlayer *media_player); - - public: #endif bool try_send_log_message(int level, const char *tag, const char *line); void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { if (!this->service_call_subscription_) return; - this->send_homeassistant_service_response(call); + this->send_message(call); } #ifdef USE_BLUETOOTH_PROXY void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override; @@ -308,7 +149,7 @@ class APIConnection : public APIServerConnection { #ifdef USE_HOMEASSISTANT_TIME void send_time_request() { GetTimeRequest req; - this->send_get_time_request(req); + this->send_message(req); } #endif @@ -328,36 +169,17 @@ class APIConnection : public APIServerConnection { bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel); void send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel); void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override; - - protected: - bool try_send_alarm_control_panel_state_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel); - bool try_send_alarm_control_panel_info_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel); - - public: #endif #ifdef USE_EVENT - void send_event(event::Event *event, std::string event_type); + void send_event(event::Event *event, const std::string &event_type); void send_event_info(event::Event *event); - - protected: - bool try_send_event_(event::Event *event); - bool try_send_event_(event::Event *event, std::string event_type); - bool try_send_event_info_(event::Event *event); - - public: #endif #ifdef USE_UPDATE bool send_update_state(update::UpdateEntity *update); void send_update_info(update::UpdateEntity *update); void update_command(const UpdateCommandRequest &msg) override; - - protected: - bool try_send_update_state_(update::UpdateEntity *update); - bool try_send_update_info_(update::UpdateEntity *update); - - public: #endif void on_disconnect_response(const DisconnectResponse &value) override; @@ -407,102 +229,61 @@ class APIConnection : public APIServerConnection { void on_no_setup_connection() override; ProtoWriteBuffer create_buffer(uint32_t reserve_size) override { // FIXME: ensure no recursive writes can happen - this->proto_write_buffer_.clear(); + // Get header padding size - used for both reserve and insert uint8_t header_padding = this->helper_->frame_header_padding(); + + // Get shared buffer from parent server + std::vector &shared_buf = this->parent_->get_shared_buffer_ref(); + shared_buf.clear(); // Reserve space for header padding + message + footer // - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext) // - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext) - this->proto_write_buffer_.reserve(reserve_size + header_padding + this->helper_->frame_footer_size()); - // Insert header padding bytes so message encoding starts at the correct position - this->proto_write_buffer_.insert(this->proto_write_buffer_.begin(), header_padding, 0); - return {&this->proto_write_buffer_}; + shared_buf.reserve(reserve_size + header_padding + this->helper_->frame_footer_size()); + // Resize to add header padding so message encoding starts at the correct position + shared_buf.resize(header_padding); + return {&shared_buf}; } + + // Prepare buffer for next message in batch + ProtoWriteBuffer prepare_message_buffer(uint16_t message_size, bool is_first_message) { + // Get reference to shared buffer (it maintains state between batch messages) + std::vector &shared_buf = this->parent_->get_shared_buffer_ref(); + + if (is_first_message) { + shared_buf.clear(); + } + + size_t current_size = shared_buf.size(); + + // Calculate padding to add: + // - First message: just header padding + // - Subsequent messages: footer for previous message + header padding for this message + size_t padding_to_add = is_first_message + ? this->helper_->frame_header_padding() + : this->helper_->frame_header_padding() + this->helper_->frame_footer_size(); + + // Reserve space for padding + message + shared_buf.reserve(current_size + padding_to_add + message_size); + + // Resize to add the padding bytes + shared_buf.resize(current_size + padding_to_add); + + return {&shared_buf}; + } + bool try_to_clear_buffer(bool log_out_of_space); - bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override; + bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) override; std::string get_client_combined_info() const { return this->client_combined_info_; } + // Buffer allocator methods for batch processing + ProtoWriteBuffer allocate_single_message_buffer(uint16_t size); + ProtoWriteBuffer allocate_batch_message_buffer(uint16_t size); + protected: - friend APIServer; - - /** - * Generic send entity state method to reduce code duplication. - * Only attempts to build and send the message if the transmit buffer is available. - * - * This is the base version for entities that use their current state. - * - * @param entity The entity to send state for - * @param try_send_func The function that tries to send the state - * @return True on success or message deferred, false if subscription check failed - */ - bool send_state_(esphome::EntityBase *entity, send_message_t try_send_func) { - if (!this->state_subscription_) - return false; - if (this->try_to_clear_buffer(true) && (this->*try_send_func)(entity)) { - return true; - } - this->deferred_message_queue_.defer(entity, try_send_func); - return true; - } - - /** - * Send entity state method that handles explicit state values. - * Only attempts to build and send the message if the transmit buffer is available. - * - * This method accepts a state parameter to be used instead of the entity's current state. - * It attempts to send the state with the provided value first, and if that fails due to buffer constraints, - * it defers the entity for later processing using the entity-only function. - * - * @tparam EntityT The entity type - * @tparam StateT Type of the state parameter - * @tparam Args Additional argument types (if any) - * @param entity The entity to send state for - * @param try_send_entity_func The function that tries to send the state with entity pointer only - * @param try_send_state_func The function that tries to send the state with entity and state parameters - * @param state The state value to send - * @param args Additional arguments to pass to the try_send_state_func - * @return True on success or message deferred, false if subscription check failed - */ - template - bool send_state_with_value_(EntityT *entity, bool (APIConnection::*try_send_entity_func)(EntityT *), - bool (APIConnection::*try_send_state_func)(EntityT *, StateT, Args...), StateT state, - Args... args) { - if (!this->state_subscription_) - return false; - if (this->try_to_clear_buffer(true) && (this->*try_send_state_func)(entity, state, args...)) { - return true; - } - this->deferred_message_queue_.defer(entity, reinterpret_cast(try_send_entity_func)); - return true; - } - - /** - * Generic send entity info method to reduce code duplication. - * Only attempts to build and send the message if the transmit buffer is available. - * - * @param entity The entity to send info for - * @param try_send_func The function that tries to send the info - */ - void send_info_(esphome::EntityBase *entity, send_message_t try_send_func) { - if (this->try_to_clear_buffer(true) && (this->*try_send_func)(entity)) { - return; - } - this->deferred_message_queue_.defer(entity, try_send_func); - } - - /** - * Generic function for generating entity info response messages. - * This is used to reduce duplication in the try_send_*_info functions. - * - * @param entity The entity to generate info for - * @param response The response object - * @param send_response_func Function pointer to send the response - * @return True if the message was sent successfully - */ - template - bool try_send_entity_info_(esphome::EntityBase *entity, ResponseT &response, - bool (APIServerConnectionBase::*send_response_func)(const ResponseT &)) { + // Helper function to fill common entity info fields + static void fill_entity_info_base(esphome::EntityBase *entity, InfoResponseProtoMessage &response) { // Set common fields that are shared by all entity types response.key = entity->get_object_id_hash(); response.object_id = entity->get_object_id(); @@ -514,12 +295,142 @@ class APIConnection : public APIServerConnection { response.icon = entity->get_icon(); response.disabled_by_default = entity->is_disabled_by_default(); response.entity_category = static_cast(entity->get_entity_category()); - - // Send the response using the provided send method - return (this->*send_response_func)(response); } - bool send_(const void *buf, size_t len, bool force); + // Helper function to fill common entity state fields + static void fill_entity_state_base(esphome::EntityBase *entity, StateResponseProtoMessage &response) { + response.key = entity->get_object_id_hash(); + } + + // Non-template helper to encode any ProtoMessage + static uint16_t encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn, + uint32_t remaining_size, bool is_single); + +#ifdef USE_BINARY_SENSOR + static uint16_t try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_COVER + static uint16_t try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_FAN + static uint16_t try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); + static uint16_t try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_LIGHT + static uint16_t try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_SENSOR + static uint16_t try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_SWITCH + static uint16_t try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_TEXT_SENSOR + static uint16_t try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_CLIMATE + static uint16_t try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_NUMBER + static uint16_t try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_DATETIME_DATE + static uint16_t try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); + static uint16_t try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_DATETIME_TIME + static uint16_t try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); + static uint16_t try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_DATETIME_DATETIME + static uint16_t try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_TEXT + static uint16_t try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); + static uint16_t try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_SELECT + static uint16_t try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_BUTTON + static uint16_t try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_LOCK + static uint16_t try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); + static uint16_t try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_VALVE + static uint16_t try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_MEDIA_PLAYER + static uint16_t try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_ALARM_CONTROL_PANEL + static uint16_t try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_EVENT + static uint16_t try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn, + uint32_t remaining_size, bool is_single); + static uint16_t try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); +#endif +#ifdef USE_UPDATE + static uint16_t try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + static uint16_t try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif +#ifdef USE_ESP32_CAMERA + static uint16_t try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); +#endif + + // Method for ListEntitiesDone batching + static uint16_t try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + + // Method for DisconnectRequest batching + static uint16_t try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + + // Helper function to get estimated message size for buffer pre-allocation + static uint16_t get_estimated_message_size(uint16_t message_type); enum class ConnectionState { WAITING_FOR_HELLO, @@ -529,9 +440,6 @@ class APIConnection : public APIServerConnection { bool remove_{false}; - // Buffer used to encode proto messages - // Re-use to prevent allocations - std::vector proto_write_buffer_; std::unique_ptr helper_; std::string client_info_; @@ -552,10 +460,160 @@ class APIConnection : public APIServerConnection { bool service_call_subscription_{false}; bool next_close_ = false; APIServer *parent_; - DeferredMessageQueue deferred_message_queue_; InitialStateIterator initial_state_iterator_; ListEntitiesIterator list_entities_iterator_; int state_subs_at_ = -1; + + // Function pointer type for message encoding + using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single); + + // Optimized MessageCreator class using union dispatch + class MessageCreator { + public: + // Constructor for function pointer (message_type = 0) + MessageCreator(MessageCreatorPtr ptr) : message_type_(0) { data_.ptr = ptr; } + + // Constructor for string state capture + MessageCreator(const std::string &value, uint16_t msg_type) : message_type_(msg_type) { + data_.string_ptr = new std::string(value); + } + + // Destructor + ~MessageCreator() { + // Clean up string data for string-based message types + if (uses_string_data_()) { + delete data_.string_ptr; + } + } + + // Copy constructor + MessageCreator(const MessageCreator &other) : message_type_(other.message_type_) { + if (message_type_ == 0) { + data_.ptr = other.data_.ptr; + } else if (uses_string_data_()) { + data_.string_ptr = new std::string(*other.data_.string_ptr); + } else { + data_ = other.data_; // For POD types + } + } + + // Move constructor + MessageCreator(MessageCreator &&other) noexcept : data_(other.data_), message_type_(other.message_type_) { + other.message_type_ = 0; // Reset other to function pointer type + other.data_.ptr = nullptr; + } + + // Assignment operators (needed for batch deduplication) + MessageCreator &operator=(const MessageCreator &other) { + if (this != &other) { + // Clean up current string data if needed + if (uses_string_data_()) { + delete data_.string_ptr; + } + // Copy new data + message_type_ = other.message_type_; + if (other.message_type_ == 0) { + data_.ptr = other.data_.ptr; + } else if (other.uses_string_data_()) { + data_.string_ptr = new std::string(*other.data_.string_ptr); + } else { + data_ = other.data_; + } + } + return *this; + } + + MessageCreator &operator=(MessageCreator &&other) noexcept { + if (this != &other) { + // Clean up current string data if needed + if (uses_string_data_()) { + delete data_.string_ptr; + } + // Move data + message_type_ = other.message_type_; + data_ = other.data_; + // Reset other to safe state + other.message_type_ = 0; + other.data_.ptr = nullptr; + } + return *this; + } + + // Call operator + uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) const; + + private: + // Helper to check if this message type uses heap-allocated strings + bool uses_string_data_() const { return message_type_ == EventResponse::MESSAGE_TYPE; } + union CreatorData { + MessageCreatorPtr ptr; // 8 bytes + std::string *string_ptr; // 8 bytes + } data_; // 8 bytes + uint16_t message_type_; // 2 bytes (0 = function ptr, >0 = state capture) + }; + + // Generic batching mechanism for both state updates and entity info + struct DeferredBatch { + struct BatchItem { + EntityBase *entity; // Entity pointer + MessageCreator creator; // Function that creates the message when needed + uint16_t message_type; // Message type for overhead calculation + + // Constructor for creating BatchItem + BatchItem(EntityBase *entity, MessageCreator creator, uint16_t message_type) + : entity(entity), creator(std::move(creator)), message_type(message_type) {} + }; + + std::vector items; + uint32_t batch_start_time{0}; + bool batch_scheduled{false}; + + DeferredBatch() { + // Pre-allocate capacity for typical batch sizes to avoid reallocation + items.reserve(8); + } + + // Add item to the batch + void add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type); + void clear() { + items.clear(); + batch_scheduled = false; + batch_start_time = 0; + } + bool empty() const { return items.empty(); } + }; + + DeferredBatch deferred_batch_; + uint32_t get_batch_delay_ms_() const; + // Message will use 8 more bytes than the minimum size, and typical + // MTU is 1500. Sometimes users will see as low as 1460 MTU. + // If its IPv6 the header is 40 bytes, and if its IPv4 + // the header is 20 bytes. So we have 1460 - 40 = 1420 bytes + // available for the payload. But we also need to add the size of + // the protobuf overhead, which is 8 bytes. + // + // To be safe we pick 1390 bytes as the maximum size + // to send in one go. This is the maximum size of a single packet + // that can be sent over the network. + // This is to avoid fragmentation of the packet. + static constexpr size_t MAX_PACKET_SIZE = 1390; // MTU + + bool schedule_batch_(); + void process_batch_(); + + // State for batch buffer allocation + bool batch_first_message_{false}; + + // Helper function to schedule a deferred message with known message type + bool schedule_message_(EntityBase *entity, MessageCreator creator, uint16_t message_type) { + this->deferred_batch_.add_item(entity, std::move(creator), message_type); + return this->schedule_batch_(); + } + + // Overload for function pointers (for info messages and current state reads) + bool schedule_message_(EntityBase *entity, MessageCreatorPtr function_ptr, uint16_t message_type) { + return schedule_message_(entity, MessageCreator(function_ptr), message_type); + } }; } // namespace api diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index f18f4104b6..e0eb94836d 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -1,26 +1,19 @@ #include "api_frame_helper.h" #ifdef USE_API -#include "esphome/core/log.h" +#include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" -#include "esphome/core/application.h" +#include "esphome/core/log.h" #include "proto.h" #include "api_pb2_size.h" #include +#include namespace esphome { namespace api { static const char *const TAG = "api.socket"; -/// Is the given return value (from write syscalls) a wouldblock error? -bool is_would_block(ssize_t ret) { - if (ret == -1) { - return errno == EWOULDBLOCK || errno == EAGAIN; - } - return ret == 0; -} - const char *api_error_to_str(APIError err) { // not using switch to ensure compiler doesn't try to build a big table out of it if (err == APIError::OK) { @@ -73,92 +66,154 @@ const char *api_error_to_str(APIError err) { return "UNKNOWN"; } -// Common implementation for writing raw data to socket -template -APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, - std::vector &tx_buf, const std::string &info, StateEnum &state, - StateEnum failed_state) { - // This method writes data to socket or buffers it +// Helper method to buffer data from IOVs +void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len) { + SendBuffer buffer; + buffer.data.reserve(total_write_len); + for (int i = 0; i < iovcnt; i++) { + const uint8_t *data = reinterpret_cast(iov[i].iov_base); + buffer.data.insert(buffer.data.end(), data, data + iov[i].iov_len); + } + this->tx_buf_.push_back(std::move(buffer)); +} + +// This method writes data to socket or buffers it +APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) { // Returns APIError::OK if successful (or would block, but data has been buffered) - // Returns APIError::SOCKET_WRITE_FAILED if socket write failed, and sets state to failed_state + // Returns APIError::SOCKET_WRITE_FAILED if socket write failed, and sets state to FAILED if (iovcnt == 0) return APIError::OK; // Nothing to do, success - size_t total_write_len = 0; + uint16_t total_write_len = 0; for (int i = 0; i < iovcnt; i++) { #ifdef HELPER_LOG_PACKETS ESP_LOGVV(TAG, "Sending raw: %s", format_hex_pretty(reinterpret_cast(iov[i].iov_base), iov[i].iov_len).c_str()); #endif - total_write_len += iov[i].iov_len; + total_write_len += static_cast(iov[i].iov_len); } - if (!tx_buf.empty()) { - // try to empty tx_buf first - while (!tx_buf.empty()) { - ssize_t sent = socket->write(tx_buf.data(), tx_buf.size()); - if (is_would_block(sent)) { - break; - } else if (sent == -1) { - ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", info.c_str(), errno); - state = failed_state; - return APIError::SOCKET_WRITE_FAILED; // Socket write failed - } - // TODO: inefficient if multiple packets in txbuf - // replace with deque of buffers - tx_buf.erase(tx_buf.begin(), tx_buf.begin() + sent); + // Try to send any existing buffered data first if there is any + if (!this->tx_buf_.empty()) { + APIError send_result = try_send_tx_buf_(); + // If real error occurred (not just WOULD_BLOCK), return it + if (send_result != APIError::OK && send_result != APIError::WOULD_BLOCK) { + return send_result; + } + + // If there is still data in the buffer, we can't send, buffer + // the new data and return + if (!this->tx_buf_.empty()) { + this->buffer_data_from_iov_(iov, iovcnt, total_write_len); + return APIError::OK; // Success, data buffered } } - if (!tx_buf.empty()) { - // tx buf not empty, can't write now because then stream would be inconsistent - // Reserve space upfront to avoid multiple reallocations - tx_buf.reserve(tx_buf.size() + total_write_len); - for (int i = 0; i < iovcnt; i++) { - tx_buf.insert(tx_buf.end(), reinterpret_cast(iov[i].iov_base), - reinterpret_cast(iov[i].iov_base) + iov[i].iov_len); - } - return APIError::OK; // Success, data buffered - } + // Try to send directly if no buffered data + ssize_t sent = this->socket_->writev(iov, iovcnt); - ssize_t sent = socket->writev(iov, iovcnt); - if (is_would_block(sent)) { - // operation would block, add buffer to tx_buf - // Reserve space upfront to avoid multiple reallocations - tx_buf.reserve(tx_buf.size() + total_write_len); - for (int i = 0; i < iovcnt; i++) { - tx_buf.insert(tx_buf.end(), reinterpret_cast(iov[i].iov_base), - reinterpret_cast(iov[i].iov_base) + iov[i].iov_len); + if (sent == -1) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + // Socket would block, buffer the data + this->buffer_data_from_iov_(iov, iovcnt, total_write_len); + return APIError::OK; // Success, data buffered } - return APIError::OK; // Success, data buffered - } else if (sent == -1) { - // an error occurred - ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", info.c_str(), errno); - state = failed_state; + // Socket error + ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno); + this->state_ = State::FAILED; return APIError::SOCKET_WRITE_FAILED; // Socket write failed - } else if ((size_t) sent != total_write_len) { - // partially sent, add end to tx_buf - size_t remaining = total_write_len - sent; - // Reserve space upfront to avoid multiple reallocations - tx_buf.reserve(tx_buf.size() + remaining); + } else if (static_cast(sent) < total_write_len) { + // Partially sent, buffer the remaining data + SendBuffer buffer; + uint16_t to_consume = static_cast(sent); + uint16_t remaining = total_write_len - static_cast(sent); + + buffer.data.reserve(remaining); - size_t to_consume = sent; for (int i = 0; i < iovcnt; i++) { if (to_consume >= iov[i].iov_len) { - to_consume -= iov[i].iov_len; + // This segment was fully sent + to_consume -= static_cast(iov[i].iov_len); } else { - tx_buf.insert(tx_buf.end(), reinterpret_cast(iov[i].iov_base) + to_consume, - reinterpret_cast(iov[i].iov_base) + iov[i].iov_len); + // This segment was partially sent or not sent at all + const uint8_t *data = reinterpret_cast(iov[i].iov_base) + to_consume; + uint16_t len = static_cast(iov[i].iov_len) - to_consume; + buffer.data.insert(buffer.data.end(), data, data + len); to_consume = 0; } } - return APIError::OK; // Success, data buffered + + this->tx_buf_.push_back(std::move(buffer)); } - return APIError::OK; // Success, all data sent + + return APIError::OK; // Success, all data sent or buffered } -#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, info_.c_str(), ##__VA_ARGS__) +// Common implementation for trying to send buffered data +// IMPORTANT: Caller MUST ensure tx_buf_ is not empty before calling this method +APIError APIFrameHelper::try_send_tx_buf_() { + // Try to send from tx_buf - we assume it's not empty as it's the caller's responsibility to check + bool tx_buf_empty = false; + while (!tx_buf_empty) { + // Get the first buffer in the queue + SendBuffer &front_buffer = this->tx_buf_.front(); + + // Try to send the remaining data in this buffer + ssize_t sent = this->socket_->write(front_buffer.current_data(), front_buffer.remaining()); + + if (sent == -1) { + if (errno != EWOULDBLOCK && errno != EAGAIN) { + // Real socket error (not just would block) + ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno); + this->state_ = State::FAILED; + return APIError::SOCKET_WRITE_FAILED; // Socket write failed + } + // Socket would block, we'll try again later + return APIError::WOULD_BLOCK; + } else if (sent == 0) { + // Nothing sent but not an error + return APIError::WOULD_BLOCK; + } else if (static_cast(sent) < front_buffer.remaining()) { + // Partially sent, update offset + // Cast to ensure no overflow issues with uint16_t + front_buffer.offset += static_cast(sent); + return APIError::WOULD_BLOCK; // Stop processing more buffers if we couldn't send a complete buffer + } else { + // Buffer completely sent, remove it from the queue + this->tx_buf_.pop_front(); + // Update empty status for the loop condition + tx_buf_empty = this->tx_buf_.empty(); + // Continue loop to try sending the next buffer + } + } + + return APIError::OK; // All buffers sent successfully +} + +APIError APIFrameHelper::init_common_() { + if (state_ != State::INITIALIZE || this->socket_ == nullptr) { + ESP_LOGVV(TAG, "%s: Bad state for init %d", this->info_.c_str(), (int) state_); + return APIError::BAD_STATE; + } + int err = this->socket_->setblocking(false); + if (err != 0) { + state_ = State::FAILED; + ESP_LOGVV(TAG, "%s: Setting nonblocking failed with errno %d", this->info_.c_str(), errno); + return APIError::TCP_NONBLOCKING_FAILED; + } + + int enable = 1; + err = this->socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int)); + if (err != 0) { + state_ = State::FAILED; + ESP_LOGVV(TAG, "%s: Setting nodelay failed with errno %d", this->info_.c_str(), errno); + return APIError::TCP_NODELAY_FAILED; + } + return APIError::OK; +} + +#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->info_.c_str(), ##__VA_ARGS__) // uncomment to log raw packets //#define HELPER_LOG_PACKETS @@ -206,23 +261,9 @@ std::string noise_err_to_str(int err) { /// Initialize the frame helper, returns OK if successful. APIError APINoiseFrameHelper::init() { - if (state_ != State::INITIALIZE || socket_ == nullptr) { - HELPER_LOG("Bad state for init %d", (int) state_); - return APIError::BAD_STATE; - } - int err = socket_->setblocking(false); - if (err != 0) { - state_ = State::FAILED; - HELPER_LOG("Setting nonblocking failed with errno %d", errno); - return APIError::TCP_NONBLOCKING_FAILED; - } - - int enable = 1; - err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int)); - if (err != 0) { - state_ = State::FAILED; - HELPER_LOG("Setting nodelay failed with errno %d", errno); - return APIError::TCP_NODELAY_FAILED; + APIError err = init_common_(); + if (err != APIError::OK) { + return err; } // init prologue @@ -234,17 +275,16 @@ APIError APINoiseFrameHelper::init() { /// Run through handshake messages (if in that phase) APIError APINoiseFrameHelper::loop() { APIError err = state_action_(); - if (err == APIError::WOULD_BLOCK) - return APIError::OK; - if (err != APIError::OK) + if (err != APIError::OK && err != APIError::WOULD_BLOCK) { return err; - if (!tx_buf_.empty()) { + } + if (!this->tx_buf_.empty()) { err = try_send_tx_buf_(); - if (err != APIError::OK) { + if (err != APIError::OK && err != APIError::WOULD_BLOCK) { return err; } } - return APIError::OK; + return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination } /** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter @@ -270,8 +310,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) { // read header if (rx_header_buf_len_ < 3) { // no header information yet - size_t to_read = 3 - rx_header_buf_len_; - ssize_t received = socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read); + uint8_t to_read = 3 - rx_header_buf_len_; + ssize_t received = this->socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read); if (received == -1) { if (errno == EWOULDBLOCK || errno == EAGAIN) { return APIError::WOULD_BLOCK; @@ -284,8 +324,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) { HELPER_LOG("Connection closed"); return APIError::CONNECTION_CLOSED; } - rx_header_buf_len_ += received; - if ((size_t) received != to_read) { + rx_header_buf_len_ += static_cast(received); + if (static_cast(received) != to_read) { // not a full read return APIError::WOULD_BLOCK; } @@ -317,8 +357,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) { if (rx_buf_len_ < msg_size) { // more data to read - size_t to_read = msg_size - rx_buf_len_; - ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read); + uint16_t to_read = msg_size - rx_buf_len_; + ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read); if (received == -1) { if (errno == EWOULDBLOCK || errno == EAGAIN) { return APIError::WOULD_BLOCK; @@ -331,8 +371,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) { HELPER_LOG("Connection closed"); return APIError::CONNECTION_CLOSED; } - rx_buf_len_ += received; - if ((size_t) received != to_read) { + rx_buf_len_ += static_cast(received); + if (static_cast(received) != to_read) { // not all read return APIError::WOULD_BLOCK; } @@ -381,6 +421,8 @@ APIError APINoiseFrameHelper::state_action_() { if (aerr != APIError::OK) return aerr; // ignore contents, may be used in future for flags + // Reserve space for: existing prologue + 2 size bytes + frame data + prologue_.reserve(prologue_.size() + 2 + frame.msg.size()); prologue_.push_back((uint8_t) (frame.msg.size() >> 8)); prologue_.push_back((uint8_t) frame.msg.size()); prologue_.insert(prologue_.end(), frame.msg.begin(), frame.msg.end()); @@ -389,16 +431,20 @@ APIError APINoiseFrameHelper::state_action_() { } if (state_ == State::SERVER_HELLO) { // send server hello + const std::string &name = App.get_name(); + const std::string &mac = get_mac_address(); + std::vector msg; + // Reserve space for: 1 byte proto + name + null + mac + null + msg.reserve(1 + name.size() + 1 + mac.size() + 1); + // chosen proto msg.push_back(0x01); // node name, terminated by null byte - const std::string &name = App.get_name(); const uint8_t *name_ptr = reinterpret_cast(name.c_str()); msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1); // node mac, terminated by null byte - const std::string &mac = get_mac_address(); const uint8_t *mac_ptr = reinterpret_cast(mac.c_str()); msg.insert(msg.end(), mac_ptr, mac_ptr + mac.size() + 1); @@ -505,7 +551,6 @@ void APINoiseFrameHelper::send_explicit_handshake_reject_(const std::string &rea write_frame_(data.data(), data.size()); state_ = orig_state; } - APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) { int err; APIError aerr; @@ -533,7 +578,7 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) { return APIError::CIPHERSTATE_DECRYPT_FAILED; } - size_t msg_size = mbuf.size; + uint16_t msg_size = mbuf.size; uint8_t *msg_data = frame.msg.data(); if (msg_size < 4) { state_ = State::FAILED; @@ -559,11 +604,22 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) { buffer->type = type; return APIError::OK; } -bool APINoiseFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); } APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) { - int err; - APIError aerr; - aerr = state_action_(); + std::vector *raw_buffer = buffer.get_buffer(); + uint16_t payload_len = static_cast(raw_buffer->size() - frame_header_padding_); + + // Resize to include MAC space (required for Noise encryption) + raw_buffer->resize(raw_buffer->size() + frame_footer_size_); + + // Use write_protobuf_packets with a single packet + std::vector packets; + packets.emplace_back(type, 0, payload_len); + + return write_protobuf_packets(buffer, packets); +} + +APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector &packets) { + APIError aerr = state_action_(); if (aerr != APIError::OK) { return aerr; } @@ -572,77 +628,67 @@ APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuf return APIError::WOULD_BLOCK; } + if (packets.empty()) { + return APIError::OK; + } + std::vector *raw_buffer = buffer.get_buffer(); - // Message data starts after padding - size_t payload_len = raw_buffer->size() - frame_header_padding_; - size_t padding = 0; - size_t msg_len = 4 + payload_len + padding; + this->reusable_iovs_.clear(); + this->reusable_iovs_.reserve(packets.size()); - // We need to resize to include MAC space, but we already reserved it in create_buffer - raw_buffer->resize(raw_buffer->size() + frame_footer_size_); + // We need to encrypt each packet in place + for (const auto &packet : packets) { + uint16_t type = packet.message_type; + uint16_t offset = packet.offset; + uint16_t payload_len = packet.payload_size; + uint16_t msg_len = 4 + payload_len; // type(2) + data_len(2) + payload - // Write the noise header in the padded area - // Buffer layout: - // [0] - 0x01 indicator byte - // [1-2] - Size of encrypted payload (filled after encryption) - // [3-4] - Message type (encrypted) - // [5-6] - Payload length (encrypted) - // [7...] - Actual payload data (encrypted) - uint8_t *buf_start = raw_buffer->data(); - buf_start[0] = 0x01; // indicator - // buf_start[1], buf_start[2] to be set later after encryption - const uint8_t msg_offset = 3; - buf_start[msg_offset + 0] = (uint8_t) (type >> 8); // type high byte - buf_start[msg_offset + 1] = (uint8_t) type; // type low byte - buf_start[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len high byte - buf_start[msg_offset + 3] = (uint8_t) payload_len; // data_len low byte - // payload data is already in the buffer starting at position 7 + // The buffer already has padding at offset + uint8_t *buf_start = raw_buffer->data() + offset; - NoiseBuffer mbuf; - noise_buffer_init(mbuf); - // The capacity parameter should be msg_len + frame_footer_size_ (MAC length) to allow space for encryption - noise_buffer_set_inout(mbuf, buf_start + msg_offset, msg_len, msg_len + frame_footer_size_); - err = noise_cipherstate_encrypt(send_cipher_, &mbuf); - if (err != 0) { - state_ = State::FAILED; - HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str()); - return APIError::CIPHERSTATE_ENCRYPT_FAILED; - } + // Write noise header + buf_start[0] = 0x01; // indicator + // buf_start[1], buf_start[2] to be set after encryption - size_t total_len = 3 + mbuf.size; - buf_start[1] = (uint8_t) (mbuf.size >> 8); - buf_start[2] = (uint8_t) mbuf.size; + // Write message header (to be encrypted) + const uint8_t msg_offset = 3; + buf_start[msg_offset + 0] = (uint8_t) (type >> 8); // type high byte + buf_start[msg_offset + 1] = (uint8_t) type; // type low byte + buf_start[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len high byte + buf_start[msg_offset + 3] = (uint8_t) payload_len; // data_len low byte + // payload data is already in the buffer starting at offset + 7 - struct iovec iov; - // Point iov_base to the beginning of the buffer (no unused padding in Noise) - // We send the entire frame: indicator + size + encrypted(type + data_len + payload + MAC) - iov.iov_base = buf_start; - iov.iov_len = total_len; + // Make sure we have space for MAC + // The buffer should already have been sized appropriately - // write raw to not have two packets sent if NAGLE disabled - return write_raw_(&iov, 1); -} -APIError APINoiseFrameHelper::try_send_tx_buf_() { - // try send from tx_buf - while (state_ != State::CLOSED && !tx_buf_.empty()) { - ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size()); - if (sent == -1) { - if (errno == EWOULDBLOCK || errno == EAGAIN) - break; + // Encrypt the message in place + NoiseBuffer mbuf; + noise_buffer_init(mbuf); + noise_buffer_set_inout(mbuf, buf_start + msg_offset, msg_len, msg_len + frame_footer_size_); + + int err = noise_cipherstate_encrypt(send_cipher_, &mbuf); + if (err != 0) { state_ = State::FAILED; - HELPER_LOG("Socket write failed with errno %d", errno); - return APIError::SOCKET_WRITE_FAILED; - } else if (sent == 0) { - break; + HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str()); + return APIError::CIPHERSTATE_ENCRYPT_FAILED; } - // TODO: inefficient if multiple packets in txbuf - // replace with deque of buffers - tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent); + + // Fill in the encrypted size + buf_start[1] = (uint8_t) (mbuf.size >> 8); + buf_start[2] = (uint8_t) mbuf.size; + + // Add iovec for this encrypted packet + struct iovec iov; + iov.iov_base = buf_start; + iov.iov_len = 3 + mbuf.size; // indicator + size + encrypted data + this->reusable_iovs_.push_back(iov); } - return APIError::OK; + // Send all encrypted packets in one writev call + return this->write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size()); } -APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) { + +APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) { uint8_t header[3]; header[0] = 0x01; // indicator header[1] = (uint8_t) (len >> 8); @@ -652,12 +698,12 @@ APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) { iov[0].iov_base = header; iov[0].iov_len = 3; if (len == 0) { - return write_raw_(iov, 1); + return this->write_raw_(iov, 1); } iov[1].iov_base = const_cast(data); iov[1].iov_len = len; - return write_raw_(iov, 2); + return this->write_raw_(iov, 2); } /** Initiate the data structures for the handshake. @@ -752,58 +798,25 @@ APINoiseFrameHelper::~APINoiseFrameHelper() { } } -APIError APINoiseFrameHelper::close() { - state_ = State::CLOSED; - int err = socket_->close(); - if (err == -1) - return APIError::CLOSE_FAILED; - return APIError::OK; -} -APIError APINoiseFrameHelper::shutdown(int how) { - int err = socket_->shutdown(how); - if (err == -1) - return APIError::SHUTDOWN_FAILED; - if (how == SHUT_RDWR) { - state_ = State::CLOSED; - } - return APIError::OK; -} extern "C" { // declare how noise generates random bytes (here with a good HWRNG based on the RF system) void noise_rand_bytes(void *output, size_t len) { if (!esphome::random_bytes(reinterpret_cast(output), len)) { - ESP_LOGE(TAG, "Failed to acquire random bytes, rebooting!"); + ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting"); arch_restart(); } } } -// Explicit template instantiation for Noise -template APIError APIFrameHelper::write_raw_( - const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector &tx_buf_, const std::string &info, - APINoiseFrameHelper::State &state, APINoiseFrameHelper::State failed_state); #endif // USE_API_NOISE #ifdef USE_API_PLAINTEXT /// Initialize the frame helper, returns OK if successful. APIError APIPlaintextFrameHelper::init() { - if (state_ != State::INITIALIZE || socket_ == nullptr) { - HELPER_LOG("Bad state for init %d", (int) state_); - return APIError::BAD_STATE; - } - int err = socket_->setblocking(false); - if (err != 0) { - state_ = State::FAILED; - HELPER_LOG("Setting nonblocking failed with errno %d", errno); - return APIError::TCP_NONBLOCKING_FAILED; - } - int enable = 1; - err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int)); - if (err != 0) { - state_ = State::FAILED; - HELPER_LOG("Setting nodelay failed with errno %d", errno); - return APIError::TCP_NODELAY_FAILED; + APIError err = init_common_(); + if (err != APIError::OK) { + return err; } state_ = State::DATA; @@ -814,14 +827,13 @@ APIError APIPlaintextFrameHelper::loop() { if (state_ != State::DATA) { return APIError::BAD_STATE; } - // try send pending TX data - if (!tx_buf_.empty()) { + if (!this->tx_buf_.empty()) { APIError err = try_send_tx_buf_(); - if (err != APIError::OK) { + if (err != APIError::OK && err != APIError::WOULD_BLOCK) { return err; } } - return APIError::OK; + return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination } /** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter @@ -841,12 +853,15 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) { // read header while (!rx_header_parsed_) { - uint8_t data; - // Reading one byte at a time is fastest in practice for ESP32 when - // there is no data on the wire (which is the common case). - // This results in faster failure detection compared to - // attempting to read multiple bytes at once. - ssize_t received = socket_->read(&data, 1); + // Now that we know when the socket is ready, we can read up to 3 bytes + // into the rx_header_buf_ before we have to switch back to reading + // one byte at a time to ensure we don't read past the message and + // into the next one. + + // Read directly into rx_header_buf_ at the current position + // Try to get to at least 3 bytes total (indicator + 2 varint bytes), then read one byte at a time + ssize_t received = + this->socket_->read(&rx_header_buf_[rx_header_buf_pos_], rx_header_buf_pos_ < 3 ? 3 - rx_header_buf_pos_ : 1); if (received == -1) { if (errno == EWOULDBLOCK || errno == EAGAIN) { return APIError::WOULD_BLOCK; @@ -860,64 +875,74 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) { return APIError::CONNECTION_CLOSED; } - // Successfully read a byte - - // Process byte according to current buffer position - if (rx_header_buf_pos_ == 0) { // Case 1: First byte (indicator byte) - if (data != 0x00) { + // If this was the first read, validate the indicator byte + if (rx_header_buf_pos_ == 0 && received > 0) { + if (rx_header_buf_[0] != 0x00) { state_ = State::FAILED; - HELPER_LOG("Bad indicator byte %u", data); + HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]); return APIError::BAD_INDICATOR; } - // We don't store the indicator byte, just increment position - rx_header_buf_pos_ = 1; // Set to 1 directly - continue; // Need more bytes before we can parse } - // Check buffer overflow before storing - if (rx_header_buf_pos_ == 5) { // Case 2: Buffer would overflow (5 bytes is max allowed) + rx_header_buf_pos_ += received; + + // Check for buffer overflow + if (rx_header_buf_pos_ >= sizeof(rx_header_buf_)) { state_ = State::FAILED; HELPER_LOG("Header buffer overflow"); return APIError::BAD_DATA_PACKET; } - // Store byte in buffer (adjust index to account for skipped indicator byte) - rx_header_buf_[rx_header_buf_pos_ - 1] = data; - - // Increment position after storing - rx_header_buf_pos_++; - - // Case 3: If we only have one varint byte, we need more - if (rx_header_buf_pos_ == 2) { // Have read indicator + 1 byte - continue; // Need more bytes before we can parse + // Need at least 3 bytes total (indicator + 2 varint bytes) before trying to parse + if (rx_header_buf_pos_ < 3) { + continue; } // At this point, we have at least 3 bytes total: - // - Validated indicator byte (0x00) but not stored + // - Validated indicator byte (0x00) stored at position 0 // - At least 2 bytes in the buffer for the varints // Buffer layout: - // First 1-3 bytes: Message size varint (variable length) - // - 2 bytes would only allow up to 16383, which is less than noise's 65535 + // [0]: indicator byte (0x00) + // [1-3]: Message size varint (variable length) + // - 2 bytes would only allow up to 16383, which is less than noise's UINT16_MAX (65535) // - 3 bytes allows up to 2097151, ensuring we support at least as much as noise - // Remaining 1-2 bytes: Message type varint (variable length) + // [2-5]: Message type varint (variable length) // We now attempt to parse both varints. If either is incomplete, // we'll continue reading more bytes. + // Skip indicator byte at position 0 + uint8_t varint_pos = 1; uint32_t consumed = 0; - auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[0], rx_header_buf_pos_ - 1, &consumed); + + auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed); if (!msg_size_varint.has_value()) { // not enough data there yet continue; } - rx_header_parsed_len_ = msg_size_varint->as_uint32(); + if (msg_size_varint->as_uint32() > std::numeric_limits::max()) { + state_ = State::FAILED; + HELPER_LOG("Bad packet: message size %" PRIu32 " exceeds maximum %u", msg_size_varint->as_uint32(), + std::numeric_limits::max()); + return APIError::BAD_DATA_PACKET; + } + rx_header_parsed_len_ = msg_size_varint->as_uint16(); - auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[consumed], rx_header_buf_pos_ - 1 - consumed, &consumed); + // Move to next varint position + varint_pos += consumed; + + auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed); if (!msg_type_varint.has_value()) { // not enough data there yet continue; } - rx_header_parsed_type_ = msg_type_varint->as_uint32(); + if (msg_type_varint->as_uint32() > std::numeric_limits::max()) { + state_ = State::FAILED; + HELPER_LOG("Bad packet: message type %" PRIu32 " exceeds maximum %u", msg_type_varint->as_uint32(), + std::numeric_limits::max()); + return APIError::BAD_DATA_PACKET; + } + rx_header_parsed_type_ = msg_type_varint->as_uint16(); rx_header_parsed_ = true; } // header reading done @@ -929,8 +954,8 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) { if (rx_buf_len_ < rx_header_parsed_len_) { // more data to read - size_t to_read = rx_header_parsed_len_ - rx_buf_len_; - ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read); + uint16_t to_read = rx_header_parsed_len_ - rx_buf_len_; + ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read); if (received == -1) { if (errno == EWOULDBLOCK || errno == EAGAIN) { return APIError::WOULD_BLOCK; @@ -943,8 +968,8 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) { HELPER_LOG("Connection closed"); return APIError::CONNECTION_CLOSED; } - rx_buf_len_ += received; - if ((size_t) received != to_read) { + rx_buf_len_ += static_cast(received); + if (static_cast(received) != to_read) { // not all read return APIError::WOULD_BLOCK; } @@ -962,7 +987,6 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) { rx_header_parsed_ = false; return APIError::OK; } - APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) { APIError aerr; @@ -990,7 +1014,7 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) { "Bad indicator byte"; iov[0].iov_base = (void *) msg; iov[0].iov_len = 19; - write_raw_(iov, 1); + this->write_raw_(iov, 1); } return aerr; } @@ -1001,108 +1025,89 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) { buffer->type = rx_header_parsed_type_; return APIError::OK; } -bool APIPlaintextFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); } APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) { + std::vector *raw_buffer = buffer.get_buffer(); + uint16_t payload_len = static_cast(raw_buffer->size() - frame_header_padding_); + + // Use write_protobuf_packets with a single packet + std::vector packets; + packets.emplace_back(type, 0, payload_len); + + return write_protobuf_packets(buffer, packets); +} + +APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, + const std::vector &packets) { if (state_ != State::DATA) { return APIError::BAD_STATE; } + if (packets.empty()) { + return APIError::OK; + } + std::vector *raw_buffer = buffer.get_buffer(); - // Message data starts after padding (frame_header_padding_ = 6) - size_t payload_len = raw_buffer->size() - frame_header_padding_; + this->reusable_iovs_.clear(); + this->reusable_iovs_.reserve(packets.size()); - // Calculate varint sizes for header components - size_t size_varint_len = api::ProtoSize::varint(static_cast(payload_len)); - size_t type_varint_len = api::ProtoSize::varint(static_cast(type)); - size_t total_header_len = 1 + size_varint_len + type_varint_len; + for (const auto &packet : packets) { + uint16_t type = packet.message_type; + uint16_t offset = packet.offset; + uint16_t payload_len = packet.payload_size; - if (total_header_len > frame_header_padding_) { - // Header is too large to fit in the padding - return APIError::BAD_ARG; + // Calculate varint sizes for header layout + uint8_t size_varint_len = api::ProtoSize::varint(static_cast(payload_len)); + uint8_t type_varint_len = api::ProtoSize::varint(static_cast(type)); + uint8_t total_header_len = 1 + size_varint_len + type_varint_len; + + // Calculate where to start writing the header + // The header starts at the latest possible position to minimize unused padding + // + // Example 1 (small values): total_header_len = 3, header_offset = 6 - 3 = 3 + // [0-2] - Unused padding + // [3] - 0x00 indicator byte + // [4] - Payload size varint (1 byte, for sizes 0-127) + // [5] - Message type varint (1 byte, for types 0-127) + // [6...] - Actual payload data + // + // Example 2 (medium values): total_header_len = 4, header_offset = 6 - 4 = 2 + // [0-1] - Unused padding + // [2] - 0x00 indicator byte + // [3-4] - Payload size varint (2 bytes, for sizes 128-16383) + // [5] - Message type varint (1 byte, for types 0-127) + // [6...] - Actual payload data + // + // Example 3 (large values): total_header_len = 6, header_offset = 6 - 6 = 0 + // [0] - 0x00 indicator byte + // [1-3] - Payload size varint (3 bytes, for sizes 16384-2097151) + // [4-5] - Message type varint (2 bytes, for types 128-32767) + // [6...] - Actual payload data + // + // The message starts at offset + frame_header_padding_ + // So we write the header starting at offset + frame_header_padding_ - total_header_len + uint8_t *buf_start = raw_buffer->data() + offset; + uint32_t header_offset = frame_header_padding_ - total_header_len; + + // Write the plaintext header + buf_start[header_offset] = 0x00; // indicator + + // Encode size varint directly into buffer + ProtoVarInt(payload_len).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len); + + // Encode type varint directly into buffer + ProtoVarInt(type).encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len); + + // Add iovec for this packet (header + payload) + struct iovec iov; + iov.iov_base = buf_start + header_offset; + iov.iov_len = total_header_len + payload_len; + this->reusable_iovs_.push_back(iov); } - // Calculate where to start writing the header - // The header starts at the latest possible position to minimize unused padding - // - // Example 1 (small values): total_header_len = 3, header_offset = 6 - 3 = 3 - // [0-2] - Unused padding - // [3] - 0x00 indicator byte - // [4] - Payload size varint (1 byte, for sizes 0-127) - // [5] - Message type varint (1 byte, for types 0-127) - // [6...] - Actual payload data - // - // Example 2 (medium values): total_header_len = 4, header_offset = 6 - 4 = 2 - // [0-1] - Unused padding - // [2] - 0x00 indicator byte - // [3-4] - Payload size varint (2 bytes, for sizes 128-16383) - // [5] - Message type varint (1 byte, for types 0-127) - // [6...] - Actual payload data - // - // Example 3 (large values): total_header_len = 6, header_offset = 6 - 6 = 0 - // [0] - 0x00 indicator byte - // [1-3] - Payload size varint (3 bytes, for sizes 16384-2097151) - // [4-5] - Message type varint (2 bytes, for types 128-32767) - // [6...] - Actual payload data - uint8_t *buf_start = raw_buffer->data(); - size_t header_offset = frame_header_padding_ - total_header_len; - - // Write the plaintext header - buf_start[header_offset] = 0x00; // indicator - - // Encode size varint directly into buffer - ProtoVarInt(payload_len).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len); - - // Encode type varint directly into buffer - ProtoVarInt(type).encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len); - - struct iovec iov; - // Point iov_base to the beginning of our header (skip unused padding) - // This ensures we only send the actual header and payload, not the empty padding bytes - iov.iov_base = buf_start + header_offset; - iov.iov_len = total_header_len + payload_len; - - return write_raw_(&iov, 1); -} -APIError APIPlaintextFrameHelper::try_send_tx_buf_() { - // try send from tx_buf - while (state_ != State::CLOSED && !tx_buf_.empty()) { - ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size()); - if (is_would_block(sent)) { - break; - } else if (sent == -1) { - state_ = State::FAILED; - HELPER_LOG("Socket write failed with errno %d", errno); - return APIError::SOCKET_WRITE_FAILED; - } - // TODO: inefficient if multiple packets in txbuf - // replace with deque of buffers - tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent); - } - - return APIError::OK; + // Send all packets in one writev call + return write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size()); } -APIError APIPlaintextFrameHelper::close() { - state_ = State::CLOSED; - int err = socket_->close(); - if (err == -1) - return APIError::CLOSE_FAILED; - return APIError::OK; -} -APIError APIPlaintextFrameHelper::shutdown(int how) { - int err = socket_->shutdown(how); - if (err == -1) - return APIError::SHUTDOWN_FAILED; - if (how == SHUT_RDWR) { - state_ = State::CLOSED; - } - return APIError::OK; -} - -// Explicit template instantiation for Plaintext -template APIError APIFrameHelper::write_raw_( - const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector &tx_buf_, const std::string &info, - APIPlaintextFrameHelper::State &state, APIPlaintextFrameHelper::State failed_state); #endif // USE_API_PLAINTEXT } // namespace api diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 25bfd594ec..dc71a7ca17 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include @@ -12,6 +13,7 @@ #include "api_noise_context.h" #include "esphome/components/socket/socket.h" +#include "esphome/core/application.h" namespace esphome { namespace api { @@ -21,15 +23,19 @@ class ProtoWriteBuffer; struct ReadPacketBuffer { std::vector container; uint16_t type; - size_t data_offset; - size_t data_len; + uint16_t data_offset; + uint16_t data_len; }; -struct PacketBuffer { - const std::vector container; - uint16_t type; - uint8_t data_offset; - uint8_t data_len; +// Packed packet info structure to minimize memory usage +struct PacketInfo { + uint16_t message_type; // 2 bytes + uint16_t offset; // 2 bytes (sufficient for packet size ~1460 bytes) + uint16_t payload_size; // 2 bytes (up to 65535 bytes) + uint16_t padding; // 2 byte (for alignment) + + PacketInfo(uint16_t type, uint16_t off, uint16_t size) + : message_type(type), offset(off), payload_size(size), padding(0) {} }; enum class APIError : int { @@ -62,38 +68,126 @@ const char *api_error_to_str(APIError err); class APIFrameHelper { public: + APIFrameHelper() = default; + explicit APIFrameHelper(std::unique_ptr socket) : socket_owned_(std::move(socket)) { + socket_ = socket_owned_.get(); + } virtual ~APIFrameHelper() = default; virtual APIError init() = 0; virtual APIError loop() = 0; virtual APIError read_packet(ReadPacketBuffer *buffer) = 0; - virtual bool can_write_without_blocking() = 0; - virtual APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) = 0; - virtual std::string getpeername() = 0; - virtual int getpeername(struct sockaddr *addr, socklen_t *addrlen) = 0; - virtual APIError close() = 0; - virtual APIError shutdown(int how) = 0; + bool can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); } + std::string getpeername() { return socket_->getpeername(); } + int getpeername(struct sockaddr *addr, socklen_t *addrlen) { return socket_->getpeername(addr, addrlen); } + APIError close() { + state_ = State::CLOSED; + int err = this->socket_->close(); + if (err == -1) + return APIError::CLOSE_FAILED; + return APIError::OK; + } + APIError shutdown(int how) { + int err = this->socket_->shutdown(how); + if (err == -1) + return APIError::SHUTDOWN_FAILED; + if (how == SHUT_RDWR) { + state_ = State::CLOSED; + } + return APIError::OK; + } // Give this helper a name for logging - virtual void set_log_info(std::string info) = 0; + void set_log_info(std::string info) { info_ = std::move(info); } + virtual APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) = 0; + // Write multiple protobuf packets in a single operation + // packets contains (message_type, offset, length) for each message in the buffer + // The buffer contains all messages with appropriate padding before each + virtual APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector &packets) = 0; // Get the frame header padding required by this protocol virtual uint8_t frame_header_padding() = 0; // Get the frame footer size required by this protocol virtual uint8_t frame_footer_size() = 0; + // Check if socket has data ready to read + bool is_socket_ready() const { return socket_ != nullptr && socket_->ready(); } protected: + // Struct for holding parsed frame data + struct ParsedFrame { + std::vector msg; + }; + + // Buffer containing data to be sent + struct SendBuffer { + std::vector data; + uint16_t offset{0}; // Current offset within the buffer (uint16_t to reduce memory usage) + + // Using uint16_t reduces memory usage since ESPHome API messages are limited to UINT16_MAX (65535) bytes + uint16_t remaining() const { return static_cast(data.size()) - offset; } + const uint8_t *current_data() const { return data.data() + offset; } + }; + + // Queue of data buffers to be sent + std::deque tx_buf_; + + // Common state enum for all frame helpers + // Note: Not all states are used by all implementations + // - INITIALIZE: Used by both Noise and Plaintext + // - CLIENT_HELLO, SERVER_HELLO, HANDSHAKE: Only used by Noise protocol + // - DATA: Used by both Noise and Plaintext + // - CLOSED: Used by both Noise and Plaintext + // - FAILED: Used by both Noise and Plaintext + // - EXPLICIT_REJECT: Only used by Noise protocol + enum class State { + INITIALIZE = 1, + CLIENT_HELLO = 2, // Noise only + SERVER_HELLO = 3, // Noise only + HANDSHAKE = 4, // Noise only + DATA = 5, + CLOSED = 6, + FAILED = 7, + EXPLICIT_REJECT = 8, // Noise only + }; + + // Current state of the frame helper + State state_{State::INITIALIZE}; + + // Helper name for logging + std::string info_; + + // Socket for communication + socket::Socket *socket_{nullptr}; + std::unique_ptr socket_owned_; + // Common implementation for writing raw data to socket + APIError write_raw_(const struct iovec *iov, int iovcnt); + + // Try to send data from the tx buffer + APIError try_send_tx_buf_(); + + // Helper method to buffer data from IOVs + void buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len); template APIError write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector &tx_buf, const std::string &info, StateEnum &state, StateEnum failed_state); uint8_t frame_header_padding_{0}; uint8_t frame_footer_size_{0}; + + // Reusable IOV array for write_protobuf_packets to avoid repeated allocations + std::vector reusable_iovs_; + + // Receive buffer for reading frame data + std::vector rx_buf_; + uint16_t rx_buf_len_ = 0; + + // Common initialization for both plaintext and noise protocols + APIError init_common_(); }; #ifdef USE_API_NOISE class APINoiseFrameHelper : public APIFrameHelper { public: APINoiseFrameHelper(std::unique_ptr socket, std::shared_ptr ctx) - : socket_(std::move(socket)), ctx_(std::move(ctx)) { + : APIFrameHelper(std::move(socket)), ctx_(std::move(ctx)) { // Noise header structure: // Pos 0: indicator (0x01) // Pos 1-2: encrypted payload size (16-bit big-endian) @@ -105,49 +199,26 @@ class APINoiseFrameHelper : public APIFrameHelper { APIError init() override; APIError loop() override; APIError read_packet(ReadPacketBuffer *buffer) override; - bool can_write_without_blocking() override; APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override; - std::string getpeername() override { return this->socket_->getpeername(); } - int getpeername(struct sockaddr *addr, socklen_t *addrlen) override { - return this->socket_->getpeername(addr, addrlen); - } - APIError close() override; - APIError shutdown(int how) override; - // Give this helper a name for logging - void set_log_info(std::string info) override { info_ = std::move(info); } + APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector &packets) override; // Get the frame header padding required by this protocol uint8_t frame_header_padding() override { return frame_header_padding_; } // Get the frame footer size required by this protocol uint8_t frame_footer_size() override { return frame_footer_size_; } protected: - struct ParsedFrame { - std::vector msg; - }; - APIError state_action_(); APIError try_read_frame_(ParsedFrame *frame); - APIError try_send_tx_buf_(); - APIError write_frame_(const uint8_t *data, size_t len); - inline APIError write_raw_(const struct iovec *iov, int iovcnt) { - return APIFrameHelper::write_raw_(iov, iovcnt, socket_.get(), tx_buf_, info_, state_, State::FAILED); - } + APIError write_frame_(const uint8_t *data, uint16_t len); APIError init_handshake_(); APIError check_handshake_finished_(); void send_explicit_handshake_reject_(const std::string &reason); - - std::unique_ptr socket_; - - std::string info_; // Fixed-size header buffer for noise protocol: // 1 byte for indicator + 2 bytes for message size (16-bit value, not varint) - // Note: Maximum message size is 65535, with a limit of 128 bytes during handshake phase + // Note: Maximum message size is UINT16_MAX (65535), with a limit of 128 bytes during handshake phase uint8_t rx_header_buf_[3]; - size_t rx_header_buf_len_ = 0; - std::vector rx_buf_; - size_t rx_buf_len_ = 0; + uint8_t rx_header_buf_len_ = 0; - std::vector tx_buf_; std::vector prologue_; std::shared_ptr ctx_; @@ -155,24 +226,13 @@ class APINoiseFrameHelper : public APIFrameHelper { NoiseCipherState *send_cipher_{nullptr}; NoiseCipherState *recv_cipher_{nullptr}; NoiseProtocolId nid_; - - enum class State { - INITIALIZE = 1, - CLIENT_HELLO = 2, - SERVER_HELLO = 3, - HANDSHAKE = 4, - DATA = 5, - CLOSED = 6, - FAILED = 7, - EXPLICIT_REJECT = 8, - } state_ = State::INITIALIZE; }; #endif // USE_API_NOISE #ifdef USE_API_PLAINTEXT class APIPlaintextFrameHelper : public APIFrameHelper { public: - APIPlaintextFrameHelper(std::unique_ptr socket) : socket_(std::move(socket)) { + APIPlaintextFrameHelper(std::unique_ptr socket) : APIFrameHelper(std::move(socket)) { // Plaintext header structure (worst case): // Pos 0: indicator (0x00) // Pos 1-3: payload size varint (up to 3 bytes) @@ -184,60 +244,27 @@ class APIPlaintextFrameHelper : public APIFrameHelper { APIError init() override; APIError loop() override; APIError read_packet(ReadPacketBuffer *buffer) override; - bool can_write_without_blocking() override; APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override; - std::string getpeername() override { return this->socket_->getpeername(); } - int getpeername(struct sockaddr *addr, socklen_t *addrlen) override { - return this->socket_->getpeername(addr, addrlen); - } - APIError close() override; - APIError shutdown(int how) override; - // Give this helper a name for logging - void set_log_info(std::string info) override { info_ = std::move(info); } - // Get the frame header padding required by this protocol + APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector &packets) override; uint8_t frame_header_padding() override { return frame_header_padding_; } // Get the frame footer size required by this protocol uint8_t frame_footer_size() override { return frame_footer_size_; } protected: - struct ParsedFrame { - std::vector msg; - }; - APIError try_read_frame_(ParsedFrame *frame); - APIError try_send_tx_buf_(); - inline APIError write_raw_(const struct iovec *iov, int iovcnt) { - return APIFrameHelper::write_raw_(iov, iovcnt, socket_.get(), tx_buf_, info_, state_, State::FAILED); - } - - std::unique_ptr socket_; - - std::string info_; // Fixed-size header buffer for plaintext protocol: - // We only need space for the two varints since we validate the indicator byte separately. - // To match noise protocol's maximum message size (65535), we need: - // 3 bytes for message size varint (supports up to 2097151) + 2 bytes for message type varint + // We now store the indicator byte + the two varints. + // To match noise protocol's maximum message size (UINT16_MAX = 65535), we need: + // 1 byte for indicator + 3 bytes for message size varint (supports up to 2097151) + 2 bytes for message type varint // // While varints could theoretically be up to 10 bytes each for 64-bit values, // attempting to process messages with headers that large would likely crash the // ESP32 due to memory constraints. - uint8_t rx_header_buf_[5]; // 5 bytes for varints (3 for size + 2 for type) + uint8_t rx_header_buf_[6]; // 1 byte indicator + 5 bytes for varints (3 for size + 2 for type) uint8_t rx_header_buf_pos_ = 0; bool rx_header_parsed_ = false; - uint32_t rx_header_parsed_type_ = 0; - uint32_t rx_header_parsed_len_ = 0; - - std::vector rx_buf_; - size_t rx_buf_len_ = 0; - - std::vector tx_buf_; - - enum class State { - INITIALIZE = 1, - DATA = 2, - CLOSED = 3, - FAILED = 4, - } state_ = State::INITIALIZE; + uint16_t rx_header_parsed_type_ = 0; + uint16_t rx_header_parsed_len_ = 0; }; #endif diff --git a/esphome/components/api/api_options.proto b/esphome/components/api/api_options.proto index feaf39ba15..3a547b8688 100644 --- a/esphome/components/api/api_options.proto +++ b/esphome/components/api/api_options.proto @@ -21,4 +21,5 @@ extend google.protobuf.MessageOptions { optional string ifdef = 1038; optional bool log = 1039 [default=true]; optional bool no_delay = 1040 [default=false]; + optional string base_class = 1041; } diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index e3181b6166..bde1824d71 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -96,6 +96,8 @@ template<> const char *proto_enum_to_string(enums::ColorMode v return "COLOR_MODE_UNKNOWN"; case enums::COLOR_MODE_ON_OFF: return "COLOR_MODE_ON_OFF"; + case enums::COLOR_MODE_LEGACY_BRIGHTNESS: + return "COLOR_MODE_LEGACY_BRIGHTNESS"; case enums::COLOR_MODE_BRIGHTNESS: return "COLOR_MODE_BRIGHTNESS"; case enums::COLOR_MODE_WHITE: @@ -514,6 +516,8 @@ template<> const char *proto_enum_to_string(enums::V return "VOICE_ASSISTANT_TTS_STREAM_START"; case enums::VOICE_ASSISTANT_TTS_STREAM_END: return "VOICE_ASSISTANT_TTS_STREAM_END"; + case enums::VOICE_ASSISTANT_INTENT_PROGRESS: + return "VOICE_ASSISTANT_INTENT_PROGRESS"; default: return "UNKNOWN"; } @@ -626,6 +630,7 @@ template<> const char *proto_enum_to_string(enums::UpdateC } } #endif + bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index c0927ebdc0..9d270bcdc1 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -41,7 +41,8 @@ enum FanDirection : uint32_t { enum ColorMode : uint32_t { COLOR_MODE_UNKNOWN = 0, COLOR_MODE_ON_OFF = 1, - COLOR_MODE_BRIGHTNESS = 2, + COLOR_MODE_LEGACY_BRIGHTNESS = 2, + COLOR_MODE_BRIGHTNESS = 3, COLOR_MODE_WHITE = 7, COLOR_MODE_COLOR_TEMPERATURE = 11, COLOR_MODE_COLD_WARM_WHITE = 19, @@ -207,6 +208,7 @@ enum VoiceAssistantEvent : uint32_t { VOICE_ASSISTANT_STT_VAD_END = 12, VOICE_ASSISTANT_TTS_STREAM_START = 98, VOICE_ASSISTANT_TTS_STREAM_END = 99, + VOICE_ASSISTANT_INTENT_PROGRESS = 100, }; enum VoiceAssistantTimerEvent : uint32_t { VOICE_ASSISTANT_TIMER_STARTED = 0, @@ -252,8 +254,34 @@ enum UpdateCommand : uint32_t { } // namespace enums +class InfoResponseProtoMessage : public ProtoMessage { + public: + ~InfoResponseProtoMessage() override = default; + std::string object_id{}; + uint32_t key{0}; + std::string name{}; + std::string unique_id{}; + bool disabled_by_default{false}; + std::string icon{}; + enums::EntityCategory entity_category{}; + + protected: +}; + +class StateResponseProtoMessage : public ProtoMessage { + public: + ~StateResponseProtoMessage() override = default; + uint32_t key{0}; + + protected: +}; class HelloRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 1; + static constexpr uint16_t ESTIMATED_SIZE = 17; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "hello_request"; } +#endif std::string client_info{}; uint32_t api_version_major{0}; uint32_t api_version_minor{0}; @@ -269,6 +297,11 @@ class HelloRequest : public ProtoMessage { }; class HelloResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 2; + static constexpr uint16_t ESTIMATED_SIZE = 26; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "hello_response"; } +#endif uint32_t api_version_major{0}; uint32_t api_version_minor{0}; std::string server_info{}; @@ -285,6 +318,11 @@ class HelloResponse : public ProtoMessage { }; class ConnectRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 3; + static constexpr uint16_t ESTIMATED_SIZE = 9; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "connect_request"; } +#endif std::string password{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -297,6 +335,11 @@ class ConnectRequest : public ProtoMessage { }; class ConnectResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 4; + static constexpr uint16_t ESTIMATED_SIZE = 2; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "connect_response"; } +#endif bool invalid_password{false}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -309,6 +352,11 @@ class ConnectResponse : public ProtoMessage { }; class DisconnectRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 5; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "disconnect_request"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -319,6 +367,11 @@ class DisconnectRequest : public ProtoMessage { }; class DisconnectResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 6; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "disconnect_response"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -329,6 +382,11 @@ class DisconnectResponse : public ProtoMessage { }; class PingRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 7; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "ping_request"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -339,6 +397,11 @@ class PingRequest : public ProtoMessage { }; class PingResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 8; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "ping_response"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -349,6 +412,11 @@ class PingResponse : public ProtoMessage { }; class DeviceInfoRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 9; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "device_info_request"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -359,6 +427,11 @@ class DeviceInfoRequest : public ProtoMessage { }; class DeviceInfoResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 10; + static constexpr uint16_t ESTIMATED_SIZE = 129; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "device_info_response"; } +#endif bool uses_password{false}; std::string name{}; std::string mac_address{}; @@ -390,6 +463,11 @@ class DeviceInfoResponse : public ProtoMessage { }; class ListEntitiesRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 11; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_request"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -400,6 +478,11 @@ class ListEntitiesRequest : public ProtoMessage { }; class ListEntitiesDoneResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 19; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_done_response"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -410,6 +493,11 @@ class ListEntitiesDoneResponse : public ProtoMessage { }; class SubscribeStatesRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 20; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_states_request"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -418,17 +506,15 @@ class SubscribeStatesRequest : public ProtoMessage { protected: }; -class ListEntitiesBinarySensorResponse : public ProtoMessage { +class ListEntitiesBinarySensorResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; + static constexpr uint16_t MESSAGE_TYPE = 12; + static constexpr uint16_t ESTIMATED_SIZE = 56; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_binary_sensor_response"; } +#endif std::string device_class{}; bool is_status_binary_sensor{false}; - bool disabled_by_default{false}; - std::string icon{}; - enums::EntityCategory entity_category{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -440,9 +526,13 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class BinarySensorStateResponse : public ProtoMessage { +class BinarySensorStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 21; + static constexpr uint16_t ESTIMATED_SIZE = 9; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "binary_sensor_state_response"; } +#endif bool state{false}; bool missing_state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -455,19 +545,17 @@ class BinarySensorStateResponse : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesCoverResponse : public ProtoMessage { +class ListEntitiesCoverResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; + static constexpr uint16_t MESSAGE_TYPE = 13; + static constexpr uint16_t ESTIMATED_SIZE = 62; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_cover_response"; } +#endif bool assumed_state{false}; bool supports_position{false}; bool supports_tilt{false}; std::string device_class{}; - bool disabled_by_default{false}; - std::string icon{}; - enums::EntityCategory entity_category{}; bool supports_stop{false}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -480,9 +568,13 @@ class ListEntitiesCoverResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class CoverStateResponse : public ProtoMessage { +class CoverStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 22; + static constexpr uint16_t ESTIMATED_SIZE = 19; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "cover_state_response"; } +#endif enums::LegacyCoverState legacy_state{}; float position{0.0f}; float tilt{0.0f}; @@ -499,6 +591,11 @@ class CoverStateResponse : public ProtoMessage { }; class CoverCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 30; + static constexpr uint16_t ESTIMATED_SIZE = 25; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "cover_command_request"; } +#endif uint32_t key{0}; bool has_legacy_command{false}; enums::LegacyCoverCommand legacy_command{}; @@ -517,19 +614,17 @@ class CoverCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesFanResponse : public ProtoMessage { +class ListEntitiesFanResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; + static constexpr uint16_t MESSAGE_TYPE = 14; + static constexpr uint16_t ESTIMATED_SIZE = 73; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_fan_response"; } +#endif bool supports_oscillation{false}; bool supports_speed{false}; bool supports_direction{false}; int32_t supported_speed_count{0}; - bool disabled_by_default{false}; - std::string icon{}; - enums::EntityCategory entity_category{}; std::vector supported_preset_modes{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -542,9 +637,13 @@ class ListEntitiesFanResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class FanStateResponse : public ProtoMessage { +class FanStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 23; + static constexpr uint16_t ESTIMATED_SIZE = 26; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "fan_state_response"; } +#endif bool state{false}; bool oscillating{false}; enums::FanSpeed speed{}; @@ -564,6 +663,11 @@ class FanStateResponse : public ProtoMessage { }; class FanCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 31; + static constexpr uint16_t ESTIMATED_SIZE = 38; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "fan_command_request"; } +#endif uint32_t key{0}; bool has_state{false}; bool state{false}; @@ -588,12 +692,13 @@ class FanCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesLightResponse : public ProtoMessage { +class ListEntitiesLightResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; + static constexpr uint16_t MESSAGE_TYPE = 15; + static constexpr uint16_t ESTIMATED_SIZE = 85; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_light_response"; } +#endif std::vector supported_color_modes{}; bool legacy_supports_brightness{false}; bool legacy_supports_rgb{false}; @@ -602,9 +707,6 @@ class ListEntitiesLightResponse : public ProtoMessage { float min_mireds{0.0f}; float max_mireds{0.0f}; std::vector effects{}; - bool disabled_by_default{false}; - std::string icon{}; - enums::EntityCategory entity_category{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -616,9 +718,13 @@ class ListEntitiesLightResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class LightStateResponse : public ProtoMessage { +class LightStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 24; + static constexpr uint16_t ESTIMATED_SIZE = 63; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "light_state_response"; } +#endif bool state{false}; float brightness{0.0f}; enums::ColorMode color_mode{}; @@ -644,6 +750,11 @@ class LightStateResponse : public ProtoMessage { }; class LightCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 32; + static constexpr uint16_t ESTIMATED_SIZE = 107; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "light_command_request"; } +#endif uint32_t key{0}; bool has_state{false}; bool state{false}; @@ -682,21 +793,19 @@ class LightCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesSensorResponse : public ProtoMessage { +class ListEntitiesSensorResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; + static constexpr uint16_t MESSAGE_TYPE = 16; + static constexpr uint16_t ESTIMATED_SIZE = 73; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_sensor_response"; } +#endif std::string unit_of_measurement{}; int32_t accuracy_decimals{0}; bool force_update{false}; std::string device_class{}; enums::SensorStateClass state_class{}; enums::SensorLastResetType legacy_last_reset_type{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -708,9 +817,13 @@ class ListEntitiesSensorResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class SensorStateResponse : public ProtoMessage { +class SensorStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 25; + static constexpr uint16_t ESTIMATED_SIZE = 12; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "sensor_state_response"; } +#endif float state{0.0f}; bool missing_state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -723,16 +836,14 @@ class SensorStateResponse : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesSwitchResponse : public ProtoMessage { +class ListEntitiesSwitchResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; + static constexpr uint16_t MESSAGE_TYPE = 17; + static constexpr uint16_t ESTIMATED_SIZE = 56; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_switch_response"; } +#endif bool assumed_state{false}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; std::string device_class{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -745,9 +856,13 @@ class ListEntitiesSwitchResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class SwitchStateResponse : public ProtoMessage { +class SwitchStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 26; + static constexpr uint16_t ESTIMATED_SIZE = 7; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "switch_state_response"; } +#endif bool state{false}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -761,6 +876,11 @@ class SwitchStateResponse : public ProtoMessage { }; class SwitchCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 33; + static constexpr uint16_t ESTIMATED_SIZE = 7; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "switch_command_request"; } +#endif uint32_t key{0}; bool state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -773,15 +893,13 @@ class SwitchCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesTextSensorResponse : public ProtoMessage { +class ListEntitiesTextSensorResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 18; + static constexpr uint16_t ESTIMATED_SIZE = 54; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_text_sensor_response"; } +#endif std::string device_class{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -794,9 +912,13 @@ class ListEntitiesTextSensorResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class TextSensorStateResponse : public ProtoMessage { +class TextSensorStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 27; + static constexpr uint16_t ESTIMATED_SIZE = 16; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "text_sensor_state_response"; } +#endif std::string state{}; bool missing_state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -812,6 +934,11 @@ class TextSensorStateResponse : public ProtoMessage { }; class SubscribeLogsRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 28; + static constexpr uint16_t ESTIMATED_SIZE = 4; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_logs_request"; } +#endif enums::LogLevel level{}; bool dump_config{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -825,6 +952,11 @@ class SubscribeLogsRequest : public ProtoMessage { }; class SubscribeLogsResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 29; + static constexpr uint16_t ESTIMATED_SIZE = 13; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_logs_response"; } +#endif enums::LogLevel level{}; std::string message{}; bool send_failed{false}; @@ -840,6 +972,11 @@ class SubscribeLogsResponse : public ProtoMessage { }; class NoiseEncryptionSetKeyRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 124; + static constexpr uint16_t ESTIMATED_SIZE = 9; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "noise_encryption_set_key_request"; } +#endif std::string key{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -852,6 +989,11 @@ class NoiseEncryptionSetKeyRequest : public ProtoMessage { }; class NoiseEncryptionSetKeyResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 125; + static constexpr uint16_t ESTIMATED_SIZE = 2; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "noise_encryption_set_key_response"; } +#endif bool success{false}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -864,6 +1006,11 @@ class NoiseEncryptionSetKeyResponse : public ProtoMessage { }; class SubscribeHomeassistantServicesRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 34; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_homeassistant_services_request"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -887,6 +1034,11 @@ class HomeassistantServiceMap : public ProtoMessage { }; class HomeassistantServiceResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 35; + static constexpr uint16_t ESTIMATED_SIZE = 113; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "homeassistant_service_response"; } +#endif std::string service{}; std::vector data{}; std::vector data_template{}; @@ -904,6 +1056,11 @@ class HomeassistantServiceResponse : public ProtoMessage { }; class SubscribeHomeAssistantStatesRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 38; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_home_assistant_states_request"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -914,6 +1071,11 @@ class SubscribeHomeAssistantStatesRequest : public ProtoMessage { }; class SubscribeHomeAssistantStateResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 39; + static constexpr uint16_t ESTIMATED_SIZE = 20; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_home_assistant_state_response"; } +#endif std::string entity_id{}; std::string attribute{}; bool once{false}; @@ -929,6 +1091,11 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage { }; class HomeAssistantStateResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 40; + static constexpr uint16_t ESTIMATED_SIZE = 27; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "home_assistant_state_response"; } +#endif std::string entity_id{}; std::string state{}; std::string attribute{}; @@ -943,6 +1110,11 @@ class HomeAssistantStateResponse : public ProtoMessage { }; class GetTimeRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 36; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "get_time_request"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -953,6 +1125,11 @@ class GetTimeRequest : public ProtoMessage { }; class GetTimeResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 37; + static constexpr uint16_t ESTIMATED_SIZE = 5; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "get_time_response"; } +#endif uint32_t epoch_seconds{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -979,6 +1156,11 @@ class ListEntitiesServicesArgument : public ProtoMessage { }; class ListEntitiesServicesResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 41; + static constexpr uint16_t ESTIMATED_SIZE = 48; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_services_response"; } +#endif std::string name{}; uint32_t key{0}; std::vector args{}; @@ -1016,6 +1198,11 @@ class ExecuteServiceArgument : public ProtoMessage { }; class ExecuteServiceRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 42; + static constexpr uint16_t ESTIMATED_SIZE = 39; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "execute_service_request"; } +#endif uint32_t key{0}; std::vector args{}; void encode(ProtoWriteBuffer buffer) const override; @@ -1028,15 +1215,13 @@ class ExecuteServiceRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; -class ListEntitiesCameraResponse : public ProtoMessage { +class ListEntitiesCameraResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - bool disabled_by_default{false}; - std::string icon{}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 43; + static constexpr uint16_t ESTIMATED_SIZE = 45; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_camera_response"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -1050,6 +1235,11 @@ class ListEntitiesCameraResponse : public ProtoMessage { }; class CameraImageResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 44; + static constexpr uint16_t ESTIMATED_SIZE = 16; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "camera_image_response"; } +#endif uint32_t key{0}; std::string data{}; bool done{false}; @@ -1066,6 +1256,11 @@ class CameraImageResponse : public ProtoMessage { }; class CameraImageRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 45; + static constexpr uint16_t ESTIMATED_SIZE = 4; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "camera_image_request"; } +#endif bool single{false}; bool stream{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -1077,12 +1272,13 @@ class CameraImageRequest : public ProtoMessage { protected: bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesClimateResponse : public ProtoMessage { +class ListEntitiesClimateResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; + static constexpr uint16_t MESSAGE_TYPE = 46; + static constexpr uint16_t ESTIMATED_SIZE = 151; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_climate_response"; } +#endif bool supports_current_temperature{false}; bool supports_two_point_target_temperature{false}; std::vector supported_modes{}; @@ -1096,9 +1292,6 @@ class ListEntitiesClimateResponse : public ProtoMessage { std::vector supported_custom_fan_modes{}; std::vector supported_presets{}; std::vector supported_custom_presets{}; - bool disabled_by_default{false}; - std::string icon{}; - enums::EntityCategory entity_category{}; float visual_current_temperature_step{0.0f}; bool supports_current_humidity{false}; bool supports_target_humidity{false}; @@ -1115,9 +1308,13 @@ class ListEntitiesClimateResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ClimateStateResponse : public ProtoMessage { +class ClimateStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 47; + static constexpr uint16_t ESTIMATED_SIZE = 65; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "climate_state_response"; } +#endif enums::ClimateMode mode{}; float current_temperature{0.0f}; float target_temperature{0.0f}; @@ -1145,6 +1342,11 @@ class ClimateStateResponse : public ProtoMessage { }; class ClimateCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 48; + static constexpr uint16_t ESTIMATED_SIZE = 83; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "climate_command_request"; } +#endif uint32_t key{0}; bool has_mode{false}; enums::ClimateMode mode{}; @@ -1179,18 +1381,16 @@ class ClimateCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesNumberResponse : public ProtoMessage { +class ListEntitiesNumberResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; + static constexpr uint16_t MESSAGE_TYPE = 49; + static constexpr uint16_t ESTIMATED_SIZE = 80; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_number_response"; } +#endif float min_value{0.0f}; float max_value{0.0f}; float step{0.0f}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; std::string unit_of_measurement{}; enums::NumberMode mode{}; std::string device_class{}; @@ -1205,9 +1405,13 @@ class ListEntitiesNumberResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class NumberStateResponse : public ProtoMessage { +class NumberStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 50; + static constexpr uint16_t ESTIMATED_SIZE = 12; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "number_state_response"; } +#endif float state{0.0f}; bool missing_state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -1222,6 +1426,11 @@ class NumberStateResponse : public ProtoMessage { }; class NumberCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 51; + static constexpr uint16_t ESTIMATED_SIZE = 10; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "number_command_request"; } +#endif uint32_t key{0}; float state{0.0f}; void encode(ProtoWriteBuffer buffer) const override; @@ -1233,16 +1442,14 @@ class NumberCommandRequest : public ProtoMessage { protected: bool decode_32bit(uint32_t field_id, Proto32Bit value) override; }; -class ListEntitiesSelectResponse : public ProtoMessage { +class ListEntitiesSelectResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; + static constexpr uint16_t MESSAGE_TYPE = 52; + static constexpr uint16_t ESTIMATED_SIZE = 63; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_select_response"; } +#endif std::vector options{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -1254,9 +1461,13 @@ class ListEntitiesSelectResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class SelectStateResponse : public ProtoMessage { +class SelectStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 53; + static constexpr uint16_t ESTIMATED_SIZE = 16; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "select_state_response"; } +#endif std::string state{}; bool missing_state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -1272,6 +1483,11 @@ class SelectStateResponse : public ProtoMessage { }; class SelectCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 54; + static constexpr uint16_t ESTIMATED_SIZE = 14; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "select_command_request"; } +#endif uint32_t key{0}; std::string state{}; void encode(ProtoWriteBuffer buffer) const override; @@ -1284,18 +1500,16 @@ class SelectCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; -class ListEntitiesSirenResponse : public ProtoMessage { +class ListEntitiesSirenResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; + static constexpr uint16_t MESSAGE_TYPE = 55; + static constexpr uint16_t ESTIMATED_SIZE = 67; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_siren_response"; } +#endif std::vector tones{}; bool supports_duration{false}; bool supports_volume{false}; - enums::EntityCategory entity_category{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -1307,9 +1521,13 @@ class ListEntitiesSirenResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class SirenStateResponse : public ProtoMessage { +class SirenStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 56; + static constexpr uint16_t ESTIMATED_SIZE = 7; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "siren_state_response"; } +#endif bool state{false}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1323,6 +1541,11 @@ class SirenStateResponse : public ProtoMessage { }; class SirenCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 57; + static constexpr uint16_t ESTIMATED_SIZE = 33; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "siren_command_request"; } +#endif uint32_t key{0}; bool has_state{false}; bool state{false}; @@ -1343,15 +1566,13 @@ class SirenCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesLockResponse : public ProtoMessage { +class ListEntitiesLockResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 58; + static constexpr uint16_t ESTIMATED_SIZE = 60; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_lock_response"; } +#endif bool assumed_state{false}; bool supports_open{false}; bool requires_code{false}; @@ -1367,9 +1588,13 @@ class ListEntitiesLockResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class LockStateResponse : public ProtoMessage { +class LockStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 59; + static constexpr uint16_t ESTIMATED_SIZE = 7; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "lock_state_response"; } +#endif enums::LockState state{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1383,6 +1608,11 @@ class LockStateResponse : public ProtoMessage { }; class LockCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 60; + static constexpr uint16_t ESTIMATED_SIZE = 18; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "lock_command_request"; } +#endif uint32_t key{0}; enums::LockCommand command{}; bool has_code{false}; @@ -1398,15 +1628,13 @@ class LockCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesButtonResponse : public ProtoMessage { +class ListEntitiesButtonResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 61; + static constexpr uint16_t ESTIMATED_SIZE = 54; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_button_response"; } +#endif std::string device_class{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1421,6 +1649,11 @@ class ListEntitiesButtonResponse : public ProtoMessage { }; class ButtonCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 62; + static constexpr uint16_t ESTIMATED_SIZE = 5; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "button_command_request"; } +#endif uint32_t key{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1448,15 +1681,13 @@ class MediaPlayerSupportedFormat : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesMediaPlayerResponse : public ProtoMessage { +class ListEntitiesMediaPlayerResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 63; + static constexpr uint16_t ESTIMATED_SIZE = 81; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_media_player_response"; } +#endif bool supports_pause{false}; std::vector supported_formats{}; void encode(ProtoWriteBuffer buffer) const override; @@ -1470,9 +1701,13 @@ class ListEntitiesMediaPlayerResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class MediaPlayerStateResponse : public ProtoMessage { +class MediaPlayerStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 64; + static constexpr uint16_t ESTIMATED_SIZE = 14; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "media_player_state_response"; } +#endif enums::MediaPlayerState state{}; float volume{0.0f}; bool muted{false}; @@ -1488,6 +1723,11 @@ class MediaPlayerStateResponse : public ProtoMessage { }; class MediaPlayerCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 65; + static constexpr uint16_t ESTIMATED_SIZE = 31; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "media_player_command_request"; } +#endif uint32_t key{0}; bool has_command{false}; enums::MediaPlayerCommand command{}; @@ -1510,6 +1750,11 @@ class MediaPlayerCommandRequest : public ProtoMessage { }; class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 66; + static constexpr uint16_t ESTIMATED_SIZE = 4; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_bluetooth_le_advertisements_request"; } +#endif uint32_t flags{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1537,6 +1782,11 @@ class BluetoothServiceData : public ProtoMessage { }; class BluetoothLEAdvertisementResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 67; + static constexpr uint16_t ESTIMATED_SIZE = 107; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_le_advertisement_response"; } +#endif uint64_t address{0}; std::string name{}; int32_t rssi{0}; @@ -1572,6 +1822,11 @@ class BluetoothLERawAdvertisement : public ProtoMessage { }; class BluetoothLERawAdvertisementsResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 93; + static constexpr uint16_t ESTIMATED_SIZE = 34; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_le_raw_advertisements_response"; } +#endif std::vector advertisements{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1584,6 +1839,11 @@ class BluetoothLERawAdvertisementsResponse : public ProtoMessage { }; class BluetoothDeviceRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 68; + static constexpr uint16_t ESTIMATED_SIZE = 12; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_device_request"; } +#endif uint64_t address{0}; enums::BluetoothDeviceRequestType request_type{}; bool has_address_type{false}; @@ -1599,6 +1859,11 @@ class BluetoothDeviceRequest : public ProtoMessage { }; class BluetoothDeviceConnectionResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 69; + static constexpr uint16_t ESTIMATED_SIZE = 14; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_device_connection_response"; } +#endif uint64_t address{0}; bool connected{false}; uint32_t mtu{0}; @@ -1614,6 +1879,11 @@ class BluetoothDeviceConnectionResponse : public ProtoMessage { }; class BluetoothGATTGetServicesRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 70; + static constexpr uint16_t ESTIMATED_SIZE = 4; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_get_services_request"; } +#endif uint64_t address{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1670,6 +1940,11 @@ class BluetoothGATTService : public ProtoMessage { }; class BluetoothGATTGetServicesResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 71; + static constexpr uint16_t ESTIMATED_SIZE = 38; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_get_services_response"; } +#endif uint64_t address{0}; std::vector services{}; void encode(ProtoWriteBuffer buffer) const override; @@ -1684,6 +1959,11 @@ class BluetoothGATTGetServicesResponse : public ProtoMessage { }; class BluetoothGATTGetServicesDoneResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 72; + static constexpr uint16_t ESTIMATED_SIZE = 4; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_get_services_done_response"; } +#endif uint64_t address{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1696,6 +1976,11 @@ class BluetoothGATTGetServicesDoneResponse : public ProtoMessage { }; class BluetoothGATTReadRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 73; + static constexpr uint16_t ESTIMATED_SIZE = 8; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_read_request"; } +#endif uint64_t address{0}; uint32_t handle{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -1709,6 +1994,11 @@ class BluetoothGATTReadRequest : public ProtoMessage { }; class BluetoothGATTReadResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 74; + static constexpr uint16_t ESTIMATED_SIZE = 17; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_read_response"; } +#endif uint64_t address{0}; uint32_t handle{0}; std::string data{}; @@ -1724,6 +2014,11 @@ class BluetoothGATTReadResponse : public ProtoMessage { }; class BluetoothGATTWriteRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 75; + static constexpr uint16_t ESTIMATED_SIZE = 19; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_write_request"; } +#endif uint64_t address{0}; uint32_t handle{0}; bool response{false}; @@ -1740,6 +2035,11 @@ class BluetoothGATTWriteRequest : public ProtoMessage { }; class BluetoothGATTReadDescriptorRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 76; + static constexpr uint16_t ESTIMATED_SIZE = 8; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_read_descriptor_request"; } +#endif uint64_t address{0}; uint32_t handle{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -1753,6 +2053,11 @@ class BluetoothGATTReadDescriptorRequest : public ProtoMessage { }; class BluetoothGATTWriteDescriptorRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 77; + static constexpr uint16_t ESTIMATED_SIZE = 17; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_write_descriptor_request"; } +#endif uint64_t address{0}; uint32_t handle{0}; std::string data{}; @@ -1768,6 +2073,11 @@ class BluetoothGATTWriteDescriptorRequest : public ProtoMessage { }; class BluetoothGATTNotifyRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 78; + static constexpr uint16_t ESTIMATED_SIZE = 10; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_notify_request"; } +#endif uint64_t address{0}; uint32_t handle{0}; bool enable{false}; @@ -1782,6 +2092,11 @@ class BluetoothGATTNotifyRequest : public ProtoMessage { }; class BluetoothGATTNotifyDataResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 79; + static constexpr uint16_t ESTIMATED_SIZE = 17; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_notify_data_response"; } +#endif uint64_t address{0}; uint32_t handle{0}; std::string data{}; @@ -1797,6 +2112,11 @@ class BluetoothGATTNotifyDataResponse : public ProtoMessage { }; class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 80; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_bluetooth_connections_free_request"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -1807,6 +2127,11 @@ class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage { }; class BluetoothConnectionsFreeResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 81; + static constexpr uint16_t ESTIMATED_SIZE = 16; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_connections_free_response"; } +#endif uint32_t free{0}; uint32_t limit{0}; std::vector allocated{}; @@ -1821,6 +2146,11 @@ class BluetoothConnectionsFreeResponse : public ProtoMessage { }; class BluetoothGATTErrorResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 82; + static constexpr uint16_t ESTIMATED_SIZE = 12; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_error_response"; } +#endif uint64_t address{0}; uint32_t handle{0}; int32_t error{0}; @@ -1835,6 +2165,11 @@ class BluetoothGATTErrorResponse : public ProtoMessage { }; class BluetoothGATTWriteResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 83; + static constexpr uint16_t ESTIMATED_SIZE = 8; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_write_response"; } +#endif uint64_t address{0}; uint32_t handle{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -1848,6 +2183,11 @@ class BluetoothGATTWriteResponse : public ProtoMessage { }; class BluetoothGATTNotifyResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 84; + static constexpr uint16_t ESTIMATED_SIZE = 8; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_gatt_notify_response"; } +#endif uint64_t address{0}; uint32_t handle{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -1861,6 +2201,11 @@ class BluetoothGATTNotifyResponse : public ProtoMessage { }; class BluetoothDevicePairingResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 85; + static constexpr uint16_t ESTIMATED_SIZE = 10; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_device_pairing_response"; } +#endif uint64_t address{0}; bool paired{false}; int32_t error{0}; @@ -1875,6 +2220,11 @@ class BluetoothDevicePairingResponse : public ProtoMessage { }; class BluetoothDeviceUnpairingResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 86; + static constexpr uint16_t ESTIMATED_SIZE = 10; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_device_unpairing_response"; } +#endif uint64_t address{0}; bool success{false}; int32_t error{0}; @@ -1889,6 +2239,11 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage { }; class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 87; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "unsubscribe_bluetooth_le_advertisements_request"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -1899,6 +2254,11 @@ class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { }; class BluetoothDeviceClearCacheResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 88; + static constexpr uint16_t ESTIMATED_SIZE = 10; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_device_clear_cache_response"; } +#endif uint64_t address{0}; bool success{false}; int32_t error{0}; @@ -1913,6 +2273,11 @@ class BluetoothDeviceClearCacheResponse : public ProtoMessage { }; class BluetoothScannerStateResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 126; + static constexpr uint16_t ESTIMATED_SIZE = 4; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_scanner_state_response"; } +#endif enums::BluetoothScannerState state{}; enums::BluetoothScannerMode mode{}; void encode(ProtoWriteBuffer buffer) const override; @@ -1926,6 +2291,11 @@ class BluetoothScannerStateResponse : public ProtoMessage { }; class BluetoothScannerSetModeRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 127; + static constexpr uint16_t ESTIMATED_SIZE = 2; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "bluetooth_scanner_set_mode_request"; } +#endif enums::BluetoothScannerMode mode{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1938,6 +2308,11 @@ class BluetoothScannerSetModeRequest : public ProtoMessage { }; class SubscribeVoiceAssistantRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 89; + static constexpr uint16_t ESTIMATED_SIZE = 6; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "subscribe_voice_assistant_request"; } +#endif bool subscribe{false}; uint32_t flags{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -1966,6 +2341,11 @@ class VoiceAssistantAudioSettings : public ProtoMessage { }; class VoiceAssistantRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 90; + static constexpr uint16_t ESTIMATED_SIZE = 41; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_request"; } +#endif bool start{false}; std::string conversation_id{}; uint32_t flags{0}; @@ -1983,6 +2363,11 @@ class VoiceAssistantRequest : public ProtoMessage { }; class VoiceAssistantResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 91; + static constexpr uint16_t ESTIMATED_SIZE = 6; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_response"; } +#endif uint32_t port{0}; bool error{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -2009,6 +2394,11 @@ class VoiceAssistantEventData : public ProtoMessage { }; class VoiceAssistantEventResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 92; + static constexpr uint16_t ESTIMATED_SIZE = 36; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_event_response"; } +#endif enums::VoiceAssistantEvent event_type{}; std::vector data{}; void encode(ProtoWriteBuffer buffer) const override; @@ -2023,6 +2413,11 @@ class VoiceAssistantEventResponse : public ProtoMessage { }; class VoiceAssistantAudio : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 106; + static constexpr uint16_t ESTIMATED_SIZE = 11; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_audio"; } +#endif std::string data{}; bool end{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -2037,6 +2432,11 @@ class VoiceAssistantAudio : public ProtoMessage { }; class VoiceAssistantTimerEventResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 115; + static constexpr uint16_t ESTIMATED_SIZE = 30; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_timer_event_response"; } +#endif enums::VoiceAssistantTimerEvent event_type{}; std::string timer_id{}; std::string name{}; @@ -2055,6 +2455,11 @@ class VoiceAssistantTimerEventResponse : public ProtoMessage { }; class VoiceAssistantAnnounceRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 119; + static constexpr uint16_t ESTIMATED_SIZE = 29; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_announce_request"; } +#endif std::string media_id{}; std::string text{}; std::string preannounce_media_id{}; @@ -2071,6 +2476,11 @@ class VoiceAssistantAnnounceRequest : public ProtoMessage { }; class VoiceAssistantAnnounceFinished : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 120; + static constexpr uint16_t ESTIMATED_SIZE = 2; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_announce_finished"; } +#endif bool success{false}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -2097,6 +2507,11 @@ class VoiceAssistantWakeWord : public ProtoMessage { }; class VoiceAssistantConfigurationRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 121; + static constexpr uint16_t ESTIMATED_SIZE = 0; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_configuration_request"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -2107,6 +2522,11 @@ class VoiceAssistantConfigurationRequest : public ProtoMessage { }; class VoiceAssistantConfigurationResponse : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 122; + static constexpr uint16_t ESTIMATED_SIZE = 56; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_configuration_response"; } +#endif std::vector available_wake_words{}; std::vector active_wake_words{}; uint32_t max_active_wake_words{0}; @@ -2122,6 +2542,11 @@ class VoiceAssistantConfigurationResponse : public ProtoMessage { }; class VoiceAssistantSetConfiguration : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 123; + static constexpr uint16_t ESTIMATED_SIZE = 18; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "voice_assistant_set_configuration"; } +#endif std::vector active_wake_words{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -2132,15 +2557,13 @@ class VoiceAssistantSetConfiguration : public ProtoMessage { protected: bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; -class ListEntitiesAlarmControlPanelResponse : public ProtoMessage { +class ListEntitiesAlarmControlPanelResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 94; + static constexpr uint16_t ESTIMATED_SIZE = 53; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_alarm_control_panel_response"; } +#endif uint32_t supported_features{0}; bool requires_code{false}; bool requires_code_to_arm{false}; @@ -2155,9 +2578,13 @@ class ListEntitiesAlarmControlPanelResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class AlarmControlPanelStateResponse : public ProtoMessage { +class AlarmControlPanelStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 95; + static constexpr uint16_t ESTIMATED_SIZE = 7; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "alarm_control_panel_state_response"; } +#endif enums::AlarmControlPanelState state{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -2171,6 +2598,11 @@ class AlarmControlPanelStateResponse : public ProtoMessage { }; class AlarmControlPanelCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 96; + static constexpr uint16_t ESTIMATED_SIZE = 16; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "alarm_control_panel_command_request"; } +#endif uint32_t key{0}; enums::AlarmControlPanelStateCommand command{}; std::string code{}; @@ -2185,15 +2617,13 @@ class AlarmControlPanelCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesTextResponse : public ProtoMessage { +class ListEntitiesTextResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 97; + static constexpr uint16_t ESTIMATED_SIZE = 64; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_text_response"; } +#endif uint32_t min_length{0}; uint32_t max_length{0}; std::string pattern{}; @@ -2209,9 +2639,13 @@ class ListEntitiesTextResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class TextStateResponse : public ProtoMessage { +class TextStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 98; + static constexpr uint16_t ESTIMATED_SIZE = 16; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "text_state_response"; } +#endif std::string state{}; bool missing_state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -2227,6 +2661,11 @@ class TextStateResponse : public ProtoMessage { }; class TextCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 99; + static constexpr uint16_t ESTIMATED_SIZE = 14; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "text_command_request"; } +#endif uint32_t key{0}; std::string state{}; void encode(ProtoWriteBuffer buffer) const override; @@ -2239,15 +2678,13 @@ class TextCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; -class ListEntitiesDateResponse : public ProtoMessage { +class ListEntitiesDateResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 100; + static constexpr uint16_t ESTIMATED_SIZE = 45; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_date_response"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -2259,9 +2696,13 @@ class ListEntitiesDateResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class DateStateResponse : public ProtoMessage { +class DateStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 101; + static constexpr uint16_t ESTIMATED_SIZE = 19; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "date_state_response"; } +#endif bool missing_state{false}; uint32_t year{0}; uint32_t month{0}; @@ -2278,6 +2719,11 @@ class DateStateResponse : public ProtoMessage { }; class DateCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 102; + static constexpr uint16_t ESTIMATED_SIZE = 17; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "date_command_request"; } +#endif uint32_t key{0}; uint32_t year{0}; uint32_t month{0}; @@ -2292,15 +2738,13 @@ class DateCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesTimeResponse : public ProtoMessage { +class ListEntitiesTimeResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 103; + static constexpr uint16_t ESTIMATED_SIZE = 45; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_time_response"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -2312,9 +2756,13 @@ class ListEntitiesTimeResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class TimeStateResponse : public ProtoMessage { +class TimeStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 104; + static constexpr uint16_t ESTIMATED_SIZE = 19; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "time_state_response"; } +#endif bool missing_state{false}; uint32_t hour{0}; uint32_t minute{0}; @@ -2331,6 +2779,11 @@ class TimeStateResponse : public ProtoMessage { }; class TimeCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 105; + static constexpr uint16_t ESTIMATED_SIZE = 17; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "time_command_request"; } +#endif uint32_t key{0}; uint32_t hour{0}; uint32_t minute{0}; @@ -2345,15 +2798,13 @@ class TimeCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesEventResponse : public ProtoMessage { +class ListEntitiesEventResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 107; + static constexpr uint16_t ESTIMATED_SIZE = 72; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_event_response"; } +#endif std::string device_class{}; std::vector event_types{}; void encode(ProtoWriteBuffer buffer) const override; @@ -2367,9 +2818,13 @@ class ListEntitiesEventResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class EventResponse : public ProtoMessage { +class EventResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 108; + static constexpr uint16_t ESTIMATED_SIZE = 14; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "event_response"; } +#endif std::string event_type{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -2381,15 +2836,13 @@ class EventResponse : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; -class ListEntitiesValveResponse : public ProtoMessage { +class ListEntitiesValveResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 109; + static constexpr uint16_t ESTIMATED_SIZE = 60; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_valve_response"; } +#endif std::string device_class{}; bool assumed_state{false}; bool supports_position{false}; @@ -2405,9 +2858,13 @@ class ListEntitiesValveResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ValveStateResponse : public ProtoMessage { +class ValveStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 110; + static constexpr uint16_t ESTIMATED_SIZE = 12; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "valve_state_response"; } +#endif float position{0.0f}; enums::ValveOperation current_operation{}; void encode(ProtoWriteBuffer buffer) const override; @@ -2422,6 +2879,11 @@ class ValveStateResponse : public ProtoMessage { }; class ValveCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 111; + static constexpr uint16_t ESTIMATED_SIZE = 14; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "valve_command_request"; } +#endif uint32_t key{0}; bool has_position{false}; float position{0.0f}; @@ -2436,15 +2898,13 @@ class ValveCommandRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ListEntitiesDateTimeResponse : public ProtoMessage { +class ListEntitiesDateTimeResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 112; + static constexpr uint16_t ESTIMATED_SIZE = 45; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_date_time_response"; } +#endif void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -2456,9 +2916,13 @@ class ListEntitiesDateTimeResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class DateTimeStateResponse : public ProtoMessage { +class DateTimeStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 113; + static constexpr uint16_t ESTIMATED_SIZE = 12; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "date_time_state_response"; } +#endif bool missing_state{false}; uint32_t epoch_seconds{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -2473,6 +2937,11 @@ class DateTimeStateResponse : public ProtoMessage { }; class DateTimeCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 114; + static constexpr uint16_t ESTIMATED_SIZE = 10; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "date_time_command_request"; } +#endif uint32_t key{0}; uint32_t epoch_seconds{0}; void encode(ProtoWriteBuffer buffer) const override; @@ -2484,15 +2953,13 @@ class DateTimeCommandRequest : public ProtoMessage { protected: bool decode_32bit(uint32_t field_id, Proto32Bit value) override; }; -class ListEntitiesUpdateResponse : public ProtoMessage { +class ListEntitiesUpdateResponse : public InfoResponseProtoMessage { public: - std::string object_id{}; - uint32_t key{0}; - std::string name{}; - std::string unique_id{}; - std::string icon{}; - bool disabled_by_default{false}; - enums::EntityCategory entity_category{}; + static constexpr uint16_t MESSAGE_TYPE = 116; + static constexpr uint16_t ESTIMATED_SIZE = 54; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "list_entities_update_response"; } +#endif std::string device_class{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -2505,9 +2972,13 @@ class ListEntitiesUpdateResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class UpdateStateResponse : public ProtoMessage { +class UpdateStateResponse : public StateResponseProtoMessage { public: - uint32_t key{0}; + static constexpr uint16_t MESSAGE_TYPE = 117; + static constexpr uint16_t ESTIMATED_SIZE = 61; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "update_state_response"; } +#endif bool missing_state{false}; bool in_progress{false}; bool has_progress{false}; @@ -2530,6 +3001,11 @@ class UpdateStateResponse : public ProtoMessage { }; class UpdateCommandRequest : public ProtoMessage { public: + static constexpr uint16_t MESSAGE_TYPE = 118; + static constexpr uint16_t ESTIMATED_SIZE = 7; +#ifdef HAS_PROTO_MESSAGE_DUMP + static constexpr const char *message_name() { return "update_command_request"; } +#endif uint32_t key{0}; enums::UpdateCommand command{}; void encode(ProtoWriteBuffer buffer) const override; diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 5a701aeafa..dacb23c12b 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -8,688 +8,12 @@ namespace api { static const char *const TAG = "api.service"; -bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) { #ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 2); -} -bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 4); -} -bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 5); -} -bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 6); -} -bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 7); -} -bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 8); -} -bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 10); -} -bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 19); -} -#ifdef USE_BINARY_SENSOR -bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 12); -} -#endif -#ifdef USE_BINARY_SENSOR -bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 21); -} -#endif -#ifdef USE_COVER -bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 13); -} -#endif -#ifdef USE_COVER -bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 22); -} -#endif -#ifdef USE_COVER -#endif -#ifdef USE_FAN -bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 14); -} -#endif -#ifdef USE_FAN -bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 23); -} -#endif -#ifdef USE_FAN -#endif -#ifdef USE_LIGHT -bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 15); -} -#endif -#ifdef USE_LIGHT -bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 24); -} -#endif -#ifdef USE_LIGHT -#endif -#ifdef USE_SENSOR -bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 16); -} -#endif -#ifdef USE_SENSOR -bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 25); -} -#endif -#ifdef USE_SWITCH -bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 17); -} -#endif -#ifdef USE_SWITCH -bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 26); -} -#endif -#ifdef USE_SWITCH -#endif -#ifdef USE_TEXT_SENSOR -bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 18); -} -#endif -#ifdef USE_TEXT_SENSOR -bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 27); -} -#endif -bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) { - return this->send_message_(msg, 29); -} -#ifdef USE_API_NOISE -#endif -#ifdef USE_API_NOISE -bool APIServerConnectionBase::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_noise_encryption_set_key_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 125); -} -#endif -bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 35); -} -bool APIServerConnectionBase::send_subscribe_home_assistant_state_response( - const SubscribeHomeAssistantStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 39); -} -bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 36); -} -bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 37); -} -bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 41); -} -#ifdef USE_ESP32_CAMERA -bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 43); -} -#endif -#ifdef USE_ESP32_CAMERA -bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 44); -} -#endif -#ifdef USE_ESP32_CAMERA -#endif -#ifdef USE_CLIMATE -bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 46); -} -#endif -#ifdef USE_CLIMATE -bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 47); -} -#endif -#ifdef USE_CLIMATE -#endif -#ifdef USE_NUMBER -bool APIServerConnectionBase::send_list_entities_number_response(const ListEntitiesNumberResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_number_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 49); -} -#endif -#ifdef USE_NUMBER -bool APIServerConnectionBase::send_number_state_response(const NumberStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_number_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 50); -} -#endif -#ifdef USE_NUMBER -#endif -#ifdef USE_SELECT -bool APIServerConnectionBase::send_list_entities_select_response(const ListEntitiesSelectResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_select_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 52); -} -#endif -#ifdef USE_SELECT -bool APIServerConnectionBase::send_select_state_response(const SelectStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_select_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 53); -} -#endif -#ifdef USE_SELECT -#endif -#ifdef USE_SIREN -bool APIServerConnectionBase::send_list_entities_siren_response(const ListEntitiesSirenResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_siren_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 55); -} -#endif -#ifdef USE_SIREN -bool APIServerConnectionBase::send_siren_state_response(const SirenStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_siren_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 56); -} -#endif -#ifdef USE_SIREN -#endif -#ifdef USE_LOCK -bool APIServerConnectionBase::send_list_entities_lock_response(const ListEntitiesLockResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_lock_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 58); +void APIServerConnectionBase::log_send_message_(const char *name, const std::string &dump) { + ESP_LOGVV(TAG, "send_message %s: %s", name, dump.c_str()); } #endif -#ifdef USE_LOCK -bool APIServerConnectionBase::send_lock_state_response(const LockStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_lock_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 59); -} -#endif -#ifdef USE_LOCK -#endif -#ifdef USE_BUTTON -bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_button_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 61); -} -#endif -#ifdef USE_BUTTON -#endif -#ifdef USE_MEDIA_PLAYER -bool APIServerConnectionBase::send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_media_player_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 63); -} -#endif -#ifdef USE_MEDIA_PLAYER -bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayerStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_media_player_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 64); -} -#endif -#ifdef USE_MEDIA_PLAYER -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 67); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_le_raw_advertisements_response( - const BluetoothLERawAdvertisementsResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_le_raw_advertisements_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 93); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_device_connection_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 69); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 71); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_get_services_done_response( - const BluetoothGATTGetServicesDoneResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_done_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 72); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_read_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 74); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_data_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 79); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_connections_free_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 81); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_error_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 82); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_write_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 83); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 84); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_device_pairing_response(const BluetoothDevicePairingResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_device_pairing_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 85); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_device_unpairing_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 86); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_device_clear_cache_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 88); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -bool APIServerConnectionBase::send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_bluetooth_scanner_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 126); -} -#endif -#ifdef USE_BLUETOOTH_PROXY -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_VOICE_ASSISTANT -bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantRequest &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_voice_assistant_request: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 90); -} -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_VOICE_ASSISTANT -bool APIServerConnectionBase::send_voice_assistant_audio(const VoiceAssistantAudio &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_voice_assistant_audio: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 106); -} -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_VOICE_ASSISTANT -bool APIServerConnectionBase::send_voice_assistant_announce_finished(const VoiceAssistantAnnounceFinished &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_voice_assistant_announce_finished: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 120); -} -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_VOICE_ASSISTANT -bool APIServerConnectionBase::send_voice_assistant_configuration_response( - const VoiceAssistantConfigurationResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_voice_assistant_configuration_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 122); -} -#endif -#ifdef USE_VOICE_ASSISTANT -#endif -#ifdef USE_ALARM_CONTROL_PANEL -bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response( - const ListEntitiesAlarmControlPanelResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_alarm_control_panel_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 94); -} -#endif -#ifdef USE_ALARM_CONTROL_PANEL -bool APIServerConnectionBase::send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_alarm_control_panel_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 95); -} -#endif -#ifdef USE_ALARM_CONTROL_PANEL -#endif -#ifdef USE_TEXT -bool APIServerConnectionBase::send_list_entities_text_response(const ListEntitiesTextResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_text_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 97); -} -#endif -#ifdef USE_TEXT -bool APIServerConnectionBase::send_text_state_response(const TextStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_text_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 98); -} -#endif -#ifdef USE_TEXT -#endif -#ifdef USE_DATETIME_DATE -bool APIServerConnectionBase::send_list_entities_date_response(const ListEntitiesDateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_date_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 100); -} -#endif -#ifdef USE_DATETIME_DATE -bool APIServerConnectionBase::send_date_state_response(const DateStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_date_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 101); -} -#endif -#ifdef USE_DATETIME_DATE -#endif -#ifdef USE_DATETIME_TIME -bool APIServerConnectionBase::send_list_entities_time_response(const ListEntitiesTimeResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_time_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 103); -} -#endif -#ifdef USE_DATETIME_TIME -bool APIServerConnectionBase::send_time_state_response(const TimeStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_time_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 104); -} -#endif -#ifdef USE_DATETIME_TIME -#endif -#ifdef USE_EVENT -bool APIServerConnectionBase::send_list_entities_event_response(const ListEntitiesEventResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_event_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 107); -} -#endif -#ifdef USE_EVENT -bool APIServerConnectionBase::send_event_response(const EventResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_event_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 108); -} -#endif -#ifdef USE_VALVE -bool APIServerConnectionBase::send_list_entities_valve_response(const ListEntitiesValveResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_valve_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 109); -} -#endif -#ifdef USE_VALVE -bool APIServerConnectionBase::send_valve_state_response(const ValveStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_valve_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 110); -} -#endif -#ifdef USE_VALVE -#endif -#ifdef USE_DATETIME_DATETIME -bool APIServerConnectionBase::send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_date_time_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 112); -} -#endif -#ifdef USE_DATETIME_DATETIME -bool APIServerConnectionBase::send_date_time_state_response(const DateTimeStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_date_time_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 113); -} -#endif -#ifdef USE_DATETIME_DATETIME -#endif -#ifdef USE_UPDATE -bool APIServerConnectionBase::send_list_entities_update_response(const ListEntitiesUpdateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_list_entities_update_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 116); -} -#endif -#ifdef USE_UPDATE -bool APIServerConnectionBase::send_update_state_response(const UpdateStateResponse &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_update_state_response: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 117); -} -#endif -#ifdef USE_UPDATE -#endif + bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { switch (msg_type) { case 1: { @@ -1273,25 +597,25 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, void APIServerConnection::on_hello_request(const HelloRequest &msg) { HelloResponse ret = this->hello(msg); - if (!this->send_hello_response(ret)) { + if (!this->send_message(ret)) { this->on_fatal_error(); } } void APIServerConnection::on_connect_request(const ConnectRequest &msg) { ConnectResponse ret = this->connect(msg); - if (!this->send_connect_response(ret)) { + if (!this->send_message(ret)) { this->on_fatal_error(); } } void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) { DisconnectResponse ret = this->disconnect(msg); - if (!this->send_disconnect_response(ret)) { + if (!this->send_message(ret)) { this->on_fatal_error(); } } void APIServerConnection::on_ping_request(const PingRequest &msg) { PingResponse ret = this->ping(msg); - if (!this->send_ping_response(ret)) { + if (!this->send_message(ret)) { this->on_fatal_error(); } } @@ -1301,7 +625,7 @@ void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) { return; } DeviceInfoResponse ret = this->device_info(msg); - if (!this->send_device_info_response(ret)) { + if (!this->send_message(ret)) { this->on_fatal_error(); } } @@ -1367,7 +691,7 @@ void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) { return; } GetTimeResponse ret = this->get_time(msg); - if (!this->send_get_time_response(ret)) { + if (!this->send_message(ret)) { this->on_fatal_error(); } } @@ -1393,7 +717,7 @@ void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncrypt return; } NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg); - if (!this->send_noise_encryption_set_key_response(ret)) { + if (!this->send_message(ret)) { this->on_fatal_error(); } } @@ -1749,7 +1073,7 @@ void APIServerConnection::on_subscribe_bluetooth_connections_free_request( return; } BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg); - if (!this->send_bluetooth_connections_free_response(ret)) { + if (!this->send_message(ret)) { this->on_fatal_error(); } } @@ -1805,7 +1129,7 @@ void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAs return; } VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg); - if (!this->send_voice_assistant_configuration_response(ret)) { + if (!this->send_message(ret)) { this->on_fatal_error(); } } diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 8ee5c0fcf1..b2be314aaf 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -10,162 +10,94 @@ namespace api { class APIServerConnectionBase : public ProtoService { public: +#ifdef HAS_PROTO_MESSAGE_DUMP + protected: + void log_send_message_(const char *name, const std::string &dump); + + public: +#endif + + template bool send_message(const T &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + this->log_send_message_(T::message_name(), msg.dump()); +#endif + return this->send_message_(msg, T::MESSAGE_TYPE); + } + virtual void on_hello_request(const HelloRequest &value){}; - bool send_hello_response(const HelloResponse &msg); + virtual void on_connect_request(const ConnectRequest &value){}; - bool send_connect_response(const ConnectResponse &msg); - bool send_disconnect_request(const DisconnectRequest &msg); + virtual void on_disconnect_request(const DisconnectRequest &value){}; - bool send_disconnect_response(const DisconnectResponse &msg); virtual void on_disconnect_response(const DisconnectResponse &value){}; - bool send_ping_request(const PingRequest &msg); virtual void on_ping_request(const PingRequest &value){}; - bool send_ping_response(const PingResponse &msg); virtual void on_ping_response(const PingResponse &value){}; virtual void on_device_info_request(const DeviceInfoRequest &value){}; - bool send_device_info_response(const DeviceInfoResponse &msg); + virtual void on_list_entities_request(const ListEntitiesRequest &value){}; - bool send_list_entities_done_response(const ListEntitiesDoneResponse &msg); + virtual void on_subscribe_states_request(const SubscribeStatesRequest &value){}; -#ifdef USE_BINARY_SENSOR - bool send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg); -#endif -#ifdef USE_BINARY_SENSOR - bool send_binary_sensor_state_response(const BinarySensorStateResponse &msg); -#endif -#ifdef USE_COVER - bool send_list_entities_cover_response(const ListEntitiesCoverResponse &msg); -#endif -#ifdef USE_COVER - bool send_cover_state_response(const CoverStateResponse &msg); -#endif + #ifdef USE_COVER virtual void on_cover_command_request(const CoverCommandRequest &value){}; #endif -#ifdef USE_FAN - bool send_list_entities_fan_response(const ListEntitiesFanResponse &msg); -#endif -#ifdef USE_FAN - bool send_fan_state_response(const FanStateResponse &msg); -#endif + #ifdef USE_FAN virtual void on_fan_command_request(const FanCommandRequest &value){}; #endif -#ifdef USE_LIGHT - bool send_list_entities_light_response(const ListEntitiesLightResponse &msg); -#endif -#ifdef USE_LIGHT - bool send_light_state_response(const LightStateResponse &msg); -#endif + #ifdef USE_LIGHT virtual void on_light_command_request(const LightCommandRequest &value){}; #endif -#ifdef USE_SENSOR - bool send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg); -#endif -#ifdef USE_SENSOR - bool send_sensor_state_response(const SensorStateResponse &msg); -#endif -#ifdef USE_SWITCH - bool send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg); -#endif -#ifdef USE_SWITCH - bool send_switch_state_response(const SwitchStateResponse &msg); -#endif + #ifdef USE_SWITCH virtual void on_switch_command_request(const SwitchCommandRequest &value){}; #endif -#ifdef USE_TEXT_SENSOR - bool send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg); -#endif -#ifdef USE_TEXT_SENSOR - bool send_text_sensor_state_response(const TextSensorStateResponse &msg); -#endif + virtual void on_subscribe_logs_request(const SubscribeLogsRequest &value){}; - bool send_subscribe_logs_response(const SubscribeLogsResponse &msg); + #ifdef USE_API_NOISE virtual void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &value){}; #endif -#ifdef USE_API_NOISE - bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg); -#endif + virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){}; - bool send_homeassistant_service_response(const HomeassistantServiceResponse &msg); + virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){}; - bool send_subscribe_home_assistant_state_response(const SubscribeHomeAssistantStateResponse &msg); + virtual void on_home_assistant_state_response(const HomeAssistantStateResponse &value){}; - bool send_get_time_request(const GetTimeRequest &msg); virtual void on_get_time_request(const GetTimeRequest &value){}; - bool send_get_time_response(const GetTimeResponse &msg); virtual void on_get_time_response(const GetTimeResponse &value){}; - bool send_list_entities_services_response(const ListEntitiesServicesResponse &msg); + virtual void on_execute_service_request(const ExecuteServiceRequest &value){}; -#ifdef USE_ESP32_CAMERA - bool send_list_entities_camera_response(const ListEntitiesCameraResponse &msg); -#endif -#ifdef USE_ESP32_CAMERA - bool send_camera_image_response(const CameraImageResponse &msg); -#endif + #ifdef USE_ESP32_CAMERA virtual void on_camera_image_request(const CameraImageRequest &value){}; #endif -#ifdef USE_CLIMATE - bool send_list_entities_climate_response(const ListEntitiesClimateResponse &msg); -#endif -#ifdef USE_CLIMATE - bool send_climate_state_response(const ClimateStateResponse &msg); -#endif + #ifdef USE_CLIMATE virtual void on_climate_command_request(const ClimateCommandRequest &value){}; #endif -#ifdef USE_NUMBER - bool send_list_entities_number_response(const ListEntitiesNumberResponse &msg); -#endif -#ifdef USE_NUMBER - bool send_number_state_response(const NumberStateResponse &msg); -#endif + #ifdef USE_NUMBER virtual void on_number_command_request(const NumberCommandRequest &value){}; #endif -#ifdef USE_SELECT - bool send_list_entities_select_response(const ListEntitiesSelectResponse &msg); -#endif -#ifdef USE_SELECT - bool send_select_state_response(const SelectStateResponse &msg); -#endif + #ifdef USE_SELECT virtual void on_select_command_request(const SelectCommandRequest &value){}; #endif -#ifdef USE_SIREN - bool send_list_entities_siren_response(const ListEntitiesSirenResponse &msg); -#endif -#ifdef USE_SIREN - bool send_siren_state_response(const SirenStateResponse &msg); -#endif + #ifdef USE_SIREN virtual void on_siren_command_request(const SirenCommandRequest &value){}; #endif -#ifdef USE_LOCK - bool send_list_entities_lock_response(const ListEntitiesLockResponse &msg); -#endif -#ifdef USE_LOCK - bool send_lock_state_response(const LockStateResponse &msg); -#endif + #ifdef USE_LOCK virtual void on_lock_command_request(const LockCommandRequest &value){}; #endif -#ifdef USE_BUTTON - bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg); -#endif + #ifdef USE_BUTTON virtual void on_button_command_request(const ButtonCommandRequest &value){}; #endif -#ifdef USE_MEDIA_PLAYER - bool send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg); -#endif -#ifdef USE_MEDIA_PLAYER - bool send_media_player_state_response(const MediaPlayerStateResponse &msg); -#endif + #ifdef USE_MEDIA_PLAYER virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){}; #endif @@ -173,33 +105,19 @@ class APIServerConnectionBase : public ProtoService { virtual void on_subscribe_bluetooth_le_advertisements_request( const SubscribeBluetoothLEAdvertisementsRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_le_raw_advertisements_response(const BluetoothLERawAdvertisementsResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_bluetooth_device_request(const BluetoothDeviceRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_get_services_done_response(const BluetoothGATTGetServicesDoneResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &value){}; #endif @@ -212,49 +130,23 @@ class APIServerConnectionBase : public ProtoService { #ifdef USE_BLUETOOTH_PROXY virtual void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_device_pairing_response(const BluetoothDevicePairingResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_unsubscribe_bluetooth_le_advertisements_request( const UnsubscribeBluetoothLEAdvertisementsRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg); -#endif -#ifdef USE_BLUETOOTH_PROXY - bool send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg); -#endif + #ifdef USE_BLUETOOTH_PROXY virtual void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &value){}; #endif #ifdef USE_VOICE_ASSISTANT virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){}; #endif -#ifdef USE_VOICE_ASSISTANT - bool send_voice_assistant_request(const VoiceAssistantRequest &msg); -#endif + #ifdef USE_VOICE_ASSISTANT virtual void on_voice_assistant_response(const VoiceAssistantResponse &value){}; #endif @@ -262,7 +154,6 @@ class APIServerConnectionBase : public ProtoService { virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){}; #endif #ifdef USE_VOICE_ASSISTANT - bool send_voice_assistant_audio(const VoiceAssistantAudio &msg); virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){}; #endif #ifdef USE_VOICE_ASSISTANT @@ -271,84 +162,39 @@ class APIServerConnectionBase : public ProtoService { #ifdef USE_VOICE_ASSISTANT virtual void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &value){}; #endif -#ifdef USE_VOICE_ASSISTANT - bool send_voice_assistant_announce_finished(const VoiceAssistantAnnounceFinished &msg); -#endif + #ifdef USE_VOICE_ASSISTANT virtual void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &value){}; #endif -#ifdef USE_VOICE_ASSISTANT - bool send_voice_assistant_configuration_response(const VoiceAssistantConfigurationResponse &msg); -#endif + #ifdef USE_VOICE_ASSISTANT virtual void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &value){}; #endif -#ifdef USE_ALARM_CONTROL_PANEL - bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg); -#endif -#ifdef USE_ALARM_CONTROL_PANEL - bool send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg); -#endif + #ifdef USE_ALARM_CONTROL_PANEL virtual void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &value){}; #endif -#ifdef USE_TEXT - bool send_list_entities_text_response(const ListEntitiesTextResponse &msg); -#endif -#ifdef USE_TEXT - bool send_text_state_response(const TextStateResponse &msg); -#endif + #ifdef USE_TEXT virtual void on_text_command_request(const TextCommandRequest &value){}; #endif -#ifdef USE_DATETIME_DATE - bool send_list_entities_date_response(const ListEntitiesDateResponse &msg); -#endif -#ifdef USE_DATETIME_DATE - bool send_date_state_response(const DateStateResponse &msg); -#endif + #ifdef USE_DATETIME_DATE virtual void on_date_command_request(const DateCommandRequest &value){}; #endif -#ifdef USE_DATETIME_TIME - bool send_list_entities_time_response(const ListEntitiesTimeResponse &msg); -#endif -#ifdef USE_DATETIME_TIME - bool send_time_state_response(const TimeStateResponse &msg); -#endif + #ifdef USE_DATETIME_TIME virtual void on_time_command_request(const TimeCommandRequest &value){}; #endif -#ifdef USE_EVENT - bool send_list_entities_event_response(const ListEntitiesEventResponse &msg); -#endif -#ifdef USE_EVENT - bool send_event_response(const EventResponse &msg); -#endif -#ifdef USE_VALVE - bool send_list_entities_valve_response(const ListEntitiesValveResponse &msg); -#endif -#ifdef USE_VALVE - bool send_valve_state_response(const ValveStateResponse &msg); -#endif + #ifdef USE_VALVE virtual void on_valve_command_request(const ValveCommandRequest &value){}; #endif -#ifdef USE_DATETIME_DATETIME - bool send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg); -#endif -#ifdef USE_DATETIME_DATETIME - bool send_date_time_state_response(const DateTimeStateResponse &msg); -#endif + #ifdef USE_DATETIME_DATETIME virtual void on_date_time_command_request(const DateTimeCommandRequest &value){}; #endif -#ifdef USE_UPDATE - bool send_list_entities_update_response(const ListEntitiesUpdateResponse &msg); -#endif -#ifdef USE_UPDATE - bool send_update_state_response(const UpdateStateResponse &msg); -#endif + #ifdef USE_UPDATE virtual void on_update_command_request(const UpdateCommandRequest &value){}; #endif diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index b987b44705..17c83c54f1 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -24,10 +24,14 @@ static const char *const TAG = "api"; // APIServer APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -APIServer::APIServer() { global_api_server = this; } +APIServer::APIServer() { + global_api_server = this; + // Pre-allocate shared write buffer + shared_write_buffer_.reserve(64); +} void APIServer::setup() { - ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->setup_controller(); #ifdef USE_API_NOISE @@ -43,7 +47,7 @@ void APIServer::setup() { } #endif - this->socket_ = socket::socket_ip(SOCK_STREAM, 0); + this->socket_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections if (this->socket_ == nullptr) { ESP_LOGW(TAG, "Could not create socket"); this->mark_failed(); @@ -88,6 +92,12 @@ void APIServer::setup() { #ifdef USE_LOGGER if (logger::global_logger != nullptr) { logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) { + if (this->shutting_down_) { + // Don't try to send logs during shutdown + // as it could result in a recursion and + // we would be filling a buffer we are trying to clear + return; + } for (auto &c : this->clients_) { if (!c->remove_) c->try_send_log_message(level, tag, message); @@ -112,18 +122,20 @@ void APIServer::setup() { } void APIServer::loop() { - // Accept new clients - while (true) { - struct sockaddr_storage source_addr; - socklen_t addr_len = sizeof(source_addr); - auto sock = this->socket_->accept((struct sockaddr *) &source_addr, &addr_len); - if (!sock) - break; - ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str()); + // Accept new clients only if the socket exists and has incoming connections + if (this->socket_ && this->socket_->ready()) { + while (true) { + struct sockaddr_storage source_addr; + socklen_t addr_len = sizeof(source_addr); + auto sock = this->socket_->accept_loop_monitored((struct sockaddr *) &source_addr, &addr_len); + if (!sock) + break; + ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str()); - auto *conn = new APIConnection(std::move(sock), this); - this->clients_.emplace_back(conn); - conn->start(); + auto *conn = new APIConnection(std::move(sock), this); + this->clients_.emplace_back(conn); + conn->start(); + } } // Process clients and remove disconnected ones in a single pass @@ -155,7 +167,7 @@ void APIServer::loop() { const uint32_t now = millis(); if (!this->is_connected()) { if (now - this->last_connected_ > this->reboot_timeout_) { - ESP_LOGE(TAG, "No client connected to API. Rebooting..."); + ESP_LOGE(TAG, "No client connected; rebooting"); App.reboot(); } this->status_set_warning(); @@ -167,8 +179,10 @@ void APIServer::loop() { } void APIServer::dump_config() { - ESP_LOGCONFIG(TAG, "API Server:"); - ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); + ESP_LOGCONFIG(TAG, + "API Server:\n" + " Address: %s:%u", + network::get_use_address().c_str(), this->port_); #ifdef USE_API_NOISE ESP_LOGCONFIG(TAG, " Using noise encryption: %s", YESNO(this->noise_ctx_->has_psk())); if (!this->noise_ctx_->has_psk()) { @@ -217,7 +231,7 @@ void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool s if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_binary_sensor_state(obj, state); + c->send_binary_sensor_state(obj); } #endif @@ -253,7 +267,7 @@ void APIServer::on_sensor_update(sensor::Sensor *obj, float state) { if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_sensor_state(obj, state); + c->send_sensor_state(obj); } #endif @@ -262,7 +276,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) { if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_switch_state(obj, state); + c->send_switch_state(obj); } #endif @@ -271,7 +285,7 @@ void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_text_sensor_state(obj, state); + c->send_text_sensor_state(obj); } #endif @@ -289,7 +303,7 @@ void APIServer::on_number_update(number::Number *obj, float state) { if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_number_state(obj, state); + c->send_number_state(obj); } #endif @@ -325,7 +339,7 @@ void APIServer::on_text_update(text::Text *obj, const std::string &state) { if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_text_state(obj, state); + c->send_text_state(obj); } #endif @@ -334,7 +348,7 @@ void APIServer::on_select_update(select::Select *obj, const std::string &state, if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_select_state(obj, state); + c->send_select_state(obj); } #endif @@ -343,7 +357,7 @@ void APIServer::on_lock_update(lock::Lock *obj) { if (obj->is_internal()) return; for (auto &c : this->clients_) - c->send_lock_state(obj, obj->state); + c->send_lock_state(obj); } #endif @@ -394,6 +408,8 @@ void APIServer::set_port(uint16_t port) { this->port_ = port; } void APIServer::set_password(const std::string &password) { this->password_ = password; } +void APIServer::set_batch_delay(uint32_t batch_delay) { this->batch_delay_ = batch_delay; } + void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) { for (auto &client : this->clients_) { client->send_homeassistant_service_call(call); @@ -452,7 +468,7 @@ bool APIServer::save_noise_psk(psk_t psk, bool make_active) { ESP_LOGW(TAG, "Disconnecting all clients to reset connections"); this->set_noise_psk(psk); for (auto &c : this->clients_) { - c->send_disconnect_request(DisconnectRequest()); + c->send_message(DisconnectRequest()); } }); } @@ -472,10 +488,36 @@ void APIServer::request_time() { bool APIServer::is_connected() const { return !this->clients_.empty(); } void APIServer::on_shutdown() { - for (auto &c : this->clients_) { - c->send_disconnect_request(DisconnectRequest()); + this->shutting_down_ = true; + + // Close the listening socket to prevent new connections + if (this->socket_) { + this->socket_->close(); + this->socket_ = nullptr; } - delay(10); + + // Change batch delay to 5ms for quick flushing during shutdown + this->batch_delay_ = 5; + + // Send disconnect requests to all connected clients + for (auto &c : this->clients_) { + if (!c->send_message(DisconnectRequest())) { + // If we can't send the disconnect request directly (tx_buffer full), + // schedule it in the batch so it will be sent with the 5ms timer + c->schedule_message_(nullptr, &APIConnection::try_send_disconnect_request, DisconnectRequest::MESSAGE_TYPE); + } + } +} + +bool APIServer::teardown() { + // If network is disconnected, no point trying to flush buffers + if (!network::is_connected()) { + return true; + } + this->loop(); + + // Return true only when all clients have been torn down + return this->clients_.empty(); } } // namespace api diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index a6645b96ce..71e470d4f8 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -34,11 +34,17 @@ class APIServer : public Component, public Controller { void loop() override; void dump_config() override; void on_shutdown() override; + bool teardown() override; bool check_password(const std::string &password) const; bool uses_password() const; void set_port(uint16_t port); void set_password(const std::string &password); void set_reboot_timeout(uint32_t reboot_timeout); + void set_batch_delay(uint32_t batch_delay); + uint32_t get_batch_delay() const { return batch_delay_; } + + // Get reference to shared buffer for API connections + std::vector &get_shared_buffer_ref() { return shared_write_buffer_; } #ifdef USE_API_NOISE bool save_noise_psk(psk_t psk, bool make_active = true); @@ -136,12 +142,15 @@ class APIServer : public Component, public Controller { } protected: + bool shutting_down_ = false; std::unique_ptr socket_ = nullptr; uint16_t port_{6053}; uint32_t reboot_timeout_{300000}; + uint32_t batch_delay_{100}; uint32_t last_connected_{0}; std::vector> clients_; std::string password_; + std::vector shared_write_buffer_; // Shared proto write buffer for all connections std::vector state_subs_; std::vector user_services_; Trigger *client_connected_trigger_ = new Trigger(); diff --git a/esphome/components/api/client.py b/esphome/components/api/client.py index c61b8d5ea3..20136ef7b8 100644 --- a/esphome/components/api/client.py +++ b/esphome/components/api/client.py @@ -5,7 +5,7 @@ from datetime import datetime import logging from typing import TYPE_CHECKING, Any -from aioesphomeapi import APIClient +from aioesphomeapi import APIClient, parse_log_message from aioesphomeapi.log_runner import async_run from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__ @@ -46,9 +46,10 @@ async def async_run_logs(config: dict[str, Any], address: str) -> None: time_ = datetime.now() message: bytes = msg.message text = message.decode("utf8", "backslashreplace") - if dashboard: - text = text.replace("\033", "\\033") - print(f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]{text}") + for parsed_msg in parse_log_message( + text, f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]" + ): + print(parsed_msg.replace("\033", "\\033") if dashboard else parsed_msg) stop = await async_run(cli, on_log, name=name) try: diff --git a/esphome/components/api/homeassistant_service.h b/esphome/components/api/homeassistant_service.h index e91756c3c9..32d13b69ae 100644 --- a/esphome/components/api/homeassistant_service.h +++ b/esphome/components/api/homeassistant_service.h @@ -3,8 +3,8 @@ #include "api_server.h" #ifdef USE_API #include "api_pb2.h" -#include "esphome/core/helpers.h" #include "esphome/core/automation.h" +#include "esphome/core/helpers.h" #include namespace esphome { diff --git a/esphome/components/api/list_entities.cpp b/esphome/components/api/list_entities.cpp index 574aa2525b..ceee3f00b8 100644 --- a/esphome/components/api/list_entities.cpp +++ b/esphome/components/api/list_entities.cpp @@ -73,7 +73,7 @@ bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done( ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {} bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) { auto resp = service->encode_list_service_response(); - return this->client_->send_list_entities_services_response(resp); + return this->client_->send_message(resp); } #ifdef USE_ESP32_CAMERA diff --git a/esphome/components/api/proto.cpp b/esphome/components/api/proto.cpp index 7af2e92534..25daf17ccc 100644 --- a/esphome/components/api/proto.cpp +++ b/esphome/components/api/proto.cpp @@ -1,5 +1,6 @@ #include "proto.h" #include +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 65bef0b6f7..eb0dbc151b 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -1,8 +1,8 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include @@ -55,6 +55,7 @@ class ProtoVarInt { return {}; // Incomplete or invalid varint } + uint16_t as_uint16() const { return this->value_; } uint32_t as_uint32() const { return this->value_; } uint64_t as_uint64() const { return this->value_; } bool as_bool() const { return this->value_; } @@ -215,7 +216,7 @@ class ProtoWriteBuffer { this->buffer_->insert(this->buffer_->end(), data, data + len); } void encode_string(uint32_t field_id, const std::string &value, bool force = false) { - this->encode_string(field_id, value.data(), value.size()); + this->encode_string(field_id, value.data(), value.size(), force); } void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) { this->encode_string(field_id, reinterpret_cast(data), len, force); @@ -359,11 +360,11 @@ class ProtoService { * @return A ProtoWriteBuffer object with the reserved size. */ virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0; - virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0; + virtual bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) = 0; virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0; // Optimized method that pre-allocates buffer based on message size - template bool send_message_(const C &msg, uint32_t message_type) { + bool send_message_(const ProtoMessage &msg, uint16_t message_type) { uint32_t msg_size = 0; msg.calculate_size(msg_size); diff --git a/esphome/components/api/subscribe_state.cpp b/esphome/components/api/subscribe_state.cpp index 4b1d5ebc0d..4180435fcc 100644 --- a/esphome/components/api/subscribe_state.cpp +++ b/esphome/components/api/subscribe_state.cpp @@ -8,7 +8,7 @@ namespace api { #ifdef USE_BINARY_SENSOR bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { - return this->client_->send_binary_sensor_state(binary_sensor, binary_sensor->state); + return this->client_->send_binary_sensor_state(binary_sensor); } #endif #ifdef USE_COVER @@ -21,27 +21,21 @@ bool InitialStateIterator::on_fan(fan::Fan *fan) { return this->client_->send_fa bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); } #endif #ifdef USE_SENSOR -bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { - return this->client_->send_sensor_state(sensor, sensor->state); -} +bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { return this->client_->send_sensor_state(sensor); } #endif #ifdef USE_SWITCH -bool InitialStateIterator::on_switch(switch_::Switch *a_switch) { - return this->client_->send_switch_state(a_switch, a_switch->state); -} +bool InitialStateIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_state(a_switch); } #endif #ifdef USE_TEXT_SENSOR bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) { - return this->client_->send_text_sensor_state(text_sensor, text_sensor->state); + return this->client_->send_text_sensor_state(text_sensor); } #endif #ifdef USE_CLIMATE bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); } #endif #ifdef USE_NUMBER -bool InitialStateIterator::on_number(number::Number *number) { - return this->client_->send_number_state(number, number->state); -} +bool InitialStateIterator::on_number(number::Number *number) { return this->client_->send_number_state(number); } #endif #ifdef USE_DATETIME_DATE bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(date); } @@ -55,15 +49,13 @@ bool InitialStateIterator::on_datetime(datetime::DateTimeEntity *datetime) { } #endif #ifdef USE_TEXT -bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); } +bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text); } #endif #ifdef USE_SELECT -bool InitialStateIterator::on_select(select::Select *select) { - return this->client_->send_select_state(select, select->state); -} +bool InitialStateIterator::on_select(select::Select *select) { return this->client_->send_select_state(select); } #endif #ifdef USE_LOCK -bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); } +bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock); } #endif #ifdef USE_VALVE bool InitialStateIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_state(valve); } diff --git a/esphome/components/as3935/as3935.cpp b/esphome/components/as3935/as3935.cpp index 29fc6ee685..5e6d62b284 100644 --- a/esphome/components/as3935/as3935.cpp +++ b/esphome/components/as3935/as3935.cpp @@ -7,7 +7,7 @@ namespace as3935 { static const char *const TAG = "as3935"; void AS3935Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up AS3935..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->irq_pin_->setup(); LOG_PIN(" IRQ Pin: ", this->irq_pin_); @@ -282,7 +282,7 @@ void AS3935Component::display_oscillator(bool state, uint8_t osc) { // based on the resonance frequency of the antenna and so it should be trimmed // before the calibration is done. bool AS3935Component::calibrate_oscillator() { - ESP_LOGI(TAG, "Starting oscillators calibration..."); + ESP_LOGI(TAG, "Starting oscillators calibration"); this->write_register(CALIB_RCO, WIPE_ALL, DIRECT_COMMAND, 0); // Send command to calibrate the oscillators this->display_oscillator(true, 2); @@ -307,7 +307,7 @@ bool AS3935Component::calibrate_oscillator() { } void AS3935Component::tune_antenna() { - ESP_LOGI(TAG, "Starting antenna tuning..."); + ESP_LOGI(TAG, "Starting antenna tuning"); uint8_t div_ratio = this->read_div_ratio(); uint8_t tune_val = this->read_capacitance(); ESP_LOGI(TAG, "Division Ratio is set to: %d", div_ratio); diff --git a/esphome/components/as5600/as5600.cpp b/esphome/components/as5600/as5600.cpp index 3fe7eab58d..ff29ae5cd4 100644 --- a/esphome/components/as5600/as5600.cpp +++ b/esphome/components/as5600/as5600.cpp @@ -23,7 +23,7 @@ static const uint8_t REGISTER_AGC = 0x1A; // 8 bytes / R static const uint8_t REGISTER_MAGNITUDE = 0x1B; // 16 bytes / R void AS5600Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up AS5600..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->read_byte(REGISTER_STATUS).has_value()) { this->mark_failed(); @@ -91,15 +91,17 @@ void AS5600Component::dump_config() { LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with AS5600 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); return; } - ESP_LOGCONFIG(TAG, " Watchdog: %d", this->watchdog_); - ESP_LOGCONFIG(TAG, " Fast Filter: %d", this->fast_filter_); - ESP_LOGCONFIG(TAG, " Slow Filter: %d", this->slow_filter_); - ESP_LOGCONFIG(TAG, " Hysteresis: %d", this->hysteresis_); - ESP_LOGCONFIG(TAG, " Start Position: %d", this->start_position_); + ESP_LOGCONFIG(TAG, + " Watchdog: %d\n" + " Fast Filter: %d\n" + " Slow Filter: %d\n" + " Hysteresis: %d\n" + " Start Position: %d", + this->watchdog_, this->fast_filter_, this->slow_filter_, this->hysteresis_, this->start_position_); if (this->end_mode_ == END_MODE_POSITION) { ESP_LOGCONFIG(TAG, " End Position: %d", this->end_position_); } else { diff --git a/esphome/components/as7341/as7341.cpp b/esphome/components/as7341/as7341.cpp index 129a3f9e37..1e335f43ad 100644 --- a/esphome/components/as7341/as7341.cpp +++ b/esphome/components/as7341/as7341.cpp @@ -8,7 +8,7 @@ namespace as7341 { static const char *const TAG = "as7341"; void AS7341Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up AS7341..."); + ESP_LOGCONFIG(TAG, "Running setup"); LOG_I2C_DEVICE(this); // Verify device ID @@ -38,12 +38,14 @@ void AS7341Component::dump_config() { ESP_LOGCONFIG(TAG, "AS7341:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with AS7341 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); - ESP_LOGCONFIG(TAG, " Gain: %u", get_gain()); - ESP_LOGCONFIG(TAG, " ATIME: %u", get_atime()); - ESP_LOGCONFIG(TAG, " ASTEP: %u", get_astep()); + ESP_LOGCONFIG(TAG, + " Gain: %u\n" + " ATIME: %u\n" + " ASTEP: %u", + get_gain(), get_atime(), get_astep()); LOG_SENSOR(" ", "F1", this->f1_); LOG_SENSOR(" ", "F2", this->f2_); diff --git a/esphome/components/at581x/at581x.cpp b/esphome/components/at581x/at581x.cpp index eef457f985..b4f817b737 100644 --- a/esphome/components/at581x/at581x.cpp +++ b/esphome/components/at581x/at581x.cpp @@ -71,19 +71,22 @@ bool AT581XComponent::i2c_read_reg(uint8_t addr, uint8_t &data) { return this->read_register(addr, &data, 1) == esphome::i2c::NO_ERROR; } -void AT581XComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up AT581X..."); } +void AT581XComponent::setup() { ESP_LOGCONFIG(TAG, "Running setup"); } void AT581XComponent::dump_config() { LOG_I2C_DEVICE(this); } #define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) bool AT581XComponent::i2c_write_config() { - ESP_LOGCONFIG(TAG, "Writing new config for AT581X..."); - ESP_LOGCONFIG(TAG, "Frequency: %dMHz", this->freq_); - ESP_LOGCONFIG(TAG, "Sensing distance: %d", this->delta_); - ESP_LOGCONFIG(TAG, "Power: %dµA", this->power_); - ESP_LOGCONFIG(TAG, "Gain: %d", this->gain_); - ESP_LOGCONFIG(TAG, "Trigger base time: %dms", this->trigger_base_time_ms_); - ESP_LOGCONFIG(TAG, "Trigger keep time: %dms", this->trigger_keep_time_ms_); - ESP_LOGCONFIG(TAG, "Protect time: %dms", this->protect_time_ms_); - ESP_LOGCONFIG(TAG, "Self check time: %dms", this->self_check_time_ms_); + ESP_LOGCONFIG(TAG, + "Writing new config for AT581X\n" + "Frequency: %dMHz\n" + "Sensing distance: %d\n" + "Power: %dµA\n" + "Gain: %d\n" + "Trigger base time: %dms\n" + "Trigger keep time: %dms\n" + "Protect time: %dms\n" + "Self check time: %dms", + this->freq_, this->delta_, this->power_, this->gain_, this->trigger_base_time_ms_, + this->trigger_keep_time_ms_, this->protect_time_ms_, this->self_check_time_ms_); // Set frequency point if (!this->i2c_write_reg(FREQ_ADDR, GAIN61_VALUE)) { diff --git a/esphome/components/atm90e26/atm90e26.cpp b/esphome/components/atm90e26/atm90e26.cpp index 6743f1a442..ce254f9532 100644 --- a/esphome/components/atm90e26/atm90e26.cpp +++ b/esphome/components/atm90e26/atm90e26.cpp @@ -41,7 +41,7 @@ void ATM90E26Component::update() { } void ATM90E26Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up ATM90E26 Component..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); uint16_t mmode = 0x422; // default values for everything but L/N line current gains @@ -135,7 +135,7 @@ void ATM90E26Component::dump_config() { ESP_LOGCONFIG("", "ATM90E26:"); LOG_PIN(" CS Pin: ", this->cs_); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with ATM90E26 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Voltage A", this->voltage_sensor_); diff --git a/esphome/components/atm90e32/atm90e32.cpp b/esphome/components/atm90e32/atm90e32.cpp index f4f177587c..f05d462845 100644 --- a/esphome/components/atm90e32/atm90e32.cpp +++ b/esphome/components/atm90e32/atm90e32.cpp @@ -108,7 +108,7 @@ void ATM90E32Component::update() { } void ATM90E32Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up ATM90E32 Component..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); uint16_t mmode0 = 0x87; // 3P4W 50Hz @@ -217,7 +217,7 @@ void ATM90E32Component::dump_config() { ESP_LOGCONFIG("", "ATM90E32:"); LOG_PIN(" CS Pin: ", this->cs_); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with ATM90E32 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Voltage A", this->phase_[PHASEA].voltage_sensor_); @@ -686,7 +686,7 @@ void ATM90E32Component::restore_power_offset_calibrations_() { } void ATM90E32Component::clear_gain_calibrations() { - ESP_LOGI(TAG, "[CALIBRATION] Clearing stored gain calibrations and restoring config-defined values..."); + ESP_LOGI(TAG, "[CALIBRATION] Clearing stored gain calibrations and restoring config-defined values"); for (int phase = 0; phase < 3; phase++) { gain_phase_[phase].voltage_gain = this->phase_[phase].voltage_gain_; diff --git a/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp b/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp index c7646bcf2f..e6e049e332 100644 --- a/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +++ b/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp @@ -17,7 +17,7 @@ constexpr static const uint8_t AXS_READ_TOUCHPAD[11] = {0xb5, 0xab, 0xa5, 0x5a, } void AXS15231Touchscreen::setup() { - ESP_LOGCONFIG(TAG, "Setting up AXS15231 Touchscreen..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (this->reset_pin_ != nullptr) { this->reset_pin_->setup(); this->reset_pin_->digital_write(false); @@ -60,8 +60,10 @@ void AXS15231Touchscreen::dump_config() { LOG_I2C_DEVICE(this); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " Width: %d", this->x_raw_max_); - ESP_LOGCONFIG(TAG, " Height: %d", this->y_raw_max_); + ESP_LOGCONFIG(TAG, + " Width: %d\n" + " Height: %d", + this->x_raw_max_, this->y_raw_max_); } } // namespace axs15231 diff --git a/esphome/components/bang_bang/bang_bang_climate.cpp b/esphome/components/bang_bang/bang_bang_climate.cpp index aed97b2890..bb85b49238 100644 --- a/esphome/components/bang_bang/bang_bang_climate.cpp +++ b/esphome/components/bang_bang/bang_bang_climate.cpp @@ -194,11 +194,14 @@ Trigger<> *BangBangClimate::get_heat_trigger() const { return this->heat_trigger void BangBangClimate::set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; } void BangBangClimate::dump_config() { LOG_CLIMATE("", "Bang Bang Climate", this); - ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_)); - ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_)); - ESP_LOGCONFIG(TAG, " Supports AWAY mode: %s", YESNO(this->supports_away_)); - ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.2f°C", this->normal_config_.default_temperature_low); - ESP_LOGCONFIG(TAG, " Default Target Temperature High: %.2f°C", this->normal_config_.default_temperature_high); + ESP_LOGCONFIG(TAG, + " Supports HEAT: %s\n" + " Supports COOL: %s\n" + " Supports AWAY mode: %s\n" + " Default Target Temperature Low: %.2f°C\n" + " Default Target Temperature High: %.2f°C", + YESNO(this->supports_heat_), YESNO(this->supports_cool_), YESNO(this->supports_away_), + this->normal_config_.default_temperature_low, this->normal_config_.default_temperature_high); } BangBangClimateTargetTempConfig::BangBangClimateTargetTempConfig() = default; diff --git a/esphome/components/bedjet/bedjet_hub.cpp b/esphome/components/bedjet/bedjet_hub.cpp index fea7080de6..7ebed2e78d 100644 --- a/esphome/components/bedjet/bedjet_hub.cpp +++ b/esphome/components/bedjet/bedjet_hub.cpp @@ -484,9 +484,11 @@ void BedJetHub::loop() {} void BedJetHub::update() { this->dispatch_status_(); } void BedJetHub::dump_config() { - ESP_LOGCONFIG(TAG, "BedJet Hub '%s'", this->get_name().c_str()); - ESP_LOGCONFIG(TAG, " ble_client.app_id: %d", this->parent()->app_id); - ESP_LOGCONFIG(TAG, " ble_client.conn_id: %d", this->parent()->get_conn_id()); + ESP_LOGCONFIG(TAG, + "BedJet Hub '%s'\n" + " ble_client.app_id: %d\n" + " ble_client.conn_id: %d", + this->get_name().c_str(), this->parent()->app_id, this->parent()->get_conn_id()); LOG_UPDATE_INTERVAL(this) ESP_LOGCONFIG(TAG, " Child components (%d):", this->children_.size()); for (auto *child : this->children_) { @@ -527,7 +529,7 @@ void BedJetHub::dispatch_status_() { } if (this->timeout_ > 0 && diff > this->timeout_ && this->parent()->enabled) { - ESP_LOGW(TAG, "[%s] Timed out after %" PRId32 " sec. Retrying...", this->get_name().c_str(), this->timeout_); + ESP_LOGW(TAG, "[%s] Timed out after %" PRId32 " sec. Retrying", this->get_name().c_str(), this->timeout_); // set_enabled(false) will only close the connection if state != IDLE. this->parent()->set_state(espbt::ClientState::CONNECTING); this->parent()->set_enabled(false); diff --git a/esphome/components/beken_spi_led_strip/led_strip.cpp b/esphome/components/beken_spi_led_strip/led_strip.cpp index 04c8649b90..d4585d7d36 100644 --- a/esphome/components/beken_spi_led_strip/led_strip.cpp +++ b/esphome/components/beken_spi_led_strip/led_strip.cpp @@ -119,7 +119,7 @@ void spi_dma_tx_finish_callback(unsigned int param) { } void BekenSPILEDStripLightOutput::setup() { - ESP_LOGCONFIG(TAG, "Setting up Beken SPI LED Strip..."); + ESP_LOGCONFIG(TAG, "Running setup"); size_t buffer_size = this->get_buffer_size_(); size_t dma_buffer_size = (buffer_size * 8) + (2 * 64); @@ -256,7 +256,7 @@ void BekenSPILEDStripLightOutput::write_state(light::LightState *state) { this->last_refresh_ = now; this->mark_shown_(); - ESP_LOGVV(TAG, "Writing RGB values to bus..."); + ESP_LOGVV(TAG, "Writing RGB values to bus"); if (spi_data == nullptr) { ESP_LOGE(TAG, "SPI not initialized"); @@ -345,8 +345,10 @@ light::ESPColorView BekenSPILEDStripLightOutput::get_view_internal(int32_t index } void BekenSPILEDStripLightOutput::dump_config() { - ESP_LOGCONFIG(TAG, "Beken SPI LED Strip:"); - ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); + ESP_LOGCONFIG(TAG, + "Beken SPI LED Strip:\n" + " Pin: %u", + this->pin_); const char *rgb_order; switch (this->rgb_order_) { case ORDER_RGB: @@ -371,9 +373,11 @@ void BekenSPILEDStripLightOutput::dump_config() { rgb_order = "UNKNOWN"; break; } - ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order); - ESP_LOGCONFIG(TAG, " Max refresh rate: %" PRIu32, *this->max_refresh_rate_); - ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_); + ESP_LOGCONFIG(TAG, + " RGB Order: %s\n" + " Max refresh rate: %" PRIu32 "\n" + " Number of LEDs: %u", + rgb_order, *this->max_refresh_rate_, this->num_leds_); } float BekenSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; } diff --git a/esphome/components/bh1750/bh1750.cpp b/esphome/components/bh1750/bh1750.cpp index de6d811ed2..4b51794907 100644 --- a/esphome/components/bh1750/bh1750.cpp +++ b/esphome/components/bh1750/bh1750.cpp @@ -38,7 +38,7 @@ MTreg: */ void BH1750Sensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up BH1750 '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); uint8_t turn_on = BH1750_COMMAND_POWER_ON; if (this->write(&turn_on, 1) != i2c::ERROR_OK) { this->mark_failed(); @@ -118,7 +118,7 @@ void BH1750Sensor::dump_config() { LOG_SENSOR("", "BH1750", this); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with BH1750 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL_FOR, this->get_name().c_str()); } LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 448323da5a..ec1c4e8a0c 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -554,6 +554,7 @@ async def register_binary_sensor(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_binary_sensor(var)) + CORE.register_platform_component("binary_sensor", var) await setup_binary_sensor_core_(var, config) diff --git a/esphome/components/binary_sensor/automation.cpp b/esphome/components/binary_sensor/automation.cpp index c2e76246aa..64a0d3db8d 100644 --- a/esphome/components/binary_sensor/automation.cpp +++ b/esphome/components/binary_sensor/automation.cpp @@ -68,8 +68,7 @@ void binary_sensor::MultiClickTrigger::on_state_(bool state) { *this->at_index_ = *this->at_index_ + 1; } void binary_sensor::MultiClickTrigger::schedule_cooldown_() { - ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms...", - this->invalid_cooldown_); + ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms", this->invalid_cooldown_); this->is_in_cooldown_ = true; this->set_timeout("cooldown", this->invalid_cooldown_, [this]() { ESP_LOGV(TAG, "Multi Click: Cooldown ended, matching is now enabled again."); diff --git a/esphome/components/bl0906/bl0906.cpp b/esphome/components/bl0906/bl0906.cpp index bddb62ff64..e48715010c 100644 --- a/esphome/components/bl0906/bl0906.cpp +++ b/esphome/components/bl0906/bl0906.cpp @@ -100,7 +100,7 @@ void BL0906::handle_actions_() { for (int i = 0; i < this->action_queue_.size(); i++) { ptr_func = this->action_queue_[i]; if (ptr_func) { - ESP_LOGI(TAG, "HandleActionCallback[%d]...", i); + ESP_LOGI(TAG, "HandleActionCallback[%d]", i); (this->*ptr_func)(); } } diff --git a/esphome/components/bl0942/bl0942.cpp b/esphome/components/bl0942/bl0942.cpp index e6f96c1b19..86eff57147 100644 --- a/esphome/components/bl0942/bl0942.cpp +++ b/esphome/components/bl0942/bl0942.cpp @@ -196,14 +196,17 @@ void BL0942::received_package_(DataPacket *data) { } void BL0942::dump_config() { // NOLINT(readability-function-cognitive-complexity) - ESP_LOGCONFIG(TAG, "BL0942:"); - ESP_LOGCONFIG(TAG, " Reset: %s", TRUEFALSE(this->reset_)); - ESP_LOGCONFIG(TAG, " Address: %d", this->address_); - ESP_LOGCONFIG(TAG, " Nominal line frequency: %d Hz", this->line_freq_); - ESP_LOGCONFIG(TAG, " Current reference: %f", this->current_reference_); - ESP_LOGCONFIG(TAG, " Energy reference: %f", this->energy_reference_); - ESP_LOGCONFIG(TAG, " Power reference: %f", this->power_reference_); - ESP_LOGCONFIG(TAG, " Voltage reference: %f", this->voltage_reference_); + ESP_LOGCONFIG(TAG, + "BL0942:\n" + " Reset: %s\n" + " Address: %d\n" + " Nominal line frequency: %d Hz\n" + " Current reference: %f\n" + " Energy reference: %f\n" + " Power reference: %f\n" + " Voltage reference: %f", + TRUEFALSE(this->reset_), this->address_, this->line_freq_, this->current_reference_, + this->energy_reference_, this->power_reference_, this->voltage_reference_); LOG_SENSOR("", "Voltage", this->voltage_sensor_); LOG_SENSOR("", "Current", this->current_sensor_); LOG_SENSOR("", "Power", this->power_sensor_); diff --git a/esphome/components/bl0942/sensor.py b/esphome/components/bl0942/sensor.py index 550f534b74..f9fe7f5a5e 100644 --- a/esphome/components/bl0942/sensor.py +++ b/esphome/components/bl0942/sensor.py @@ -9,6 +9,7 @@ from esphome.const import ( CONF_ID, CONF_LINE_FREQUENCY, CONF_POWER, + CONF_RESET, CONF_VOLTAGE, DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, @@ -27,7 +28,6 @@ from esphome.const import ( CONF_CURRENT_REFERENCE = "current_reference" CONF_ENERGY_REFERENCE = "energy_reference" CONF_POWER_REFERENCE = "power_reference" -CONF_RESET = "reset" CONF_VOLTAGE_REFERENCE = "voltage_reference" DEPENDENCIES = ["uart"] diff --git a/esphome/components/ble_client/__init__.py b/esphome/components/ble_client/__init__.py index 37f8ea32b3..a88172ca87 100644 --- a/esphome/components/ble_client/__init__.py +++ b/esphome/components/ble_client/__init__.py @@ -1,7 +1,8 @@ from esphome import automation from esphome.automation import maybe_simple_id import esphome.codegen as cg -from esphome.components import esp32_ble_client, esp32_ble_tracker +from esphome.components import esp32_ble, esp32_ble_client, esp32_ble_tracker +from esphome.components.esp32_ble import BTLoggers import esphome.config_validation as cv from esphome.const import ( CONF_CHARACTERISTIC_UUID, @@ -287,6 +288,9 @@ async def remove_bond_to_code(config, action_id, template_arg, args): async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.SMP) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) await esp32_ble_tracker.register_client(var, config) diff --git a/esphome/components/ble_client/output/ble_binary_output.cpp b/esphome/components/ble_client/output/ble_binary_output.cpp index 3f05bc4b84..ce67193be7 100644 --- a/esphome/components/ble_client/output/ble_binary_output.cpp +++ b/esphome/components/ble_client/output/ble_binary_output.cpp @@ -10,9 +10,12 @@ static const char *const TAG = "ble_binary_output"; void BLEBinaryOutput::dump_config() { ESP_LOGCONFIG(TAG, "BLE Binary Output:"); - ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent_->address_str().c_str()); - ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str()); + ESP_LOGCONFIG(TAG, + " MAC address : %s\n" + " Service UUID : %s\n" + " Characteristic UUID: %s", + this->parent_->address_str().c_str(), this->service_uuid_.to_string().c_str(), + this->char_uuid_.to_string().c_str()); LOG_BINARY_OUTPUT(this); } diff --git a/esphome/components/ble_client/sensor/ble_sensor.cpp b/esphome/components/ble_client/sensor/ble_sensor.cpp index 43f61f5304..f91b07fee2 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.cpp +++ b/esphome/components/ble_client/sensor/ble_sensor.cpp @@ -1,7 +1,7 @@ #include "ble_sensor.h" -#include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #ifdef USE_ESP32 @@ -15,11 +15,14 @@ void BLESensor::loop() {} void BLESensor::dump_config() { LOG_SENSOR("", "BLE Sensor", this); - ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent()->address_str().c_str()); - ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Descriptor UUID : %s", this->descr_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Notifications : %s", YESNO(this->notify_)); + ESP_LOGCONFIG(TAG, + " MAC address : %s\n" + " Service UUID : %s\n" + " Characteristic UUID: %s\n" + " Descriptor UUID : %s\n" + " Notifications : %s", + this->parent()->address_str().c_str(), this->service_uuid_.to_string().c_str(), + this->char_uuid_.to_string().c_str(), this->descr_uuid_.to_string().c_str(), YESNO(this->notify_)); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp b/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp index 33938ee7b7..5083e235c6 100644 --- a/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +++ b/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp @@ -18,11 +18,14 @@ void BLETextSensor::loop() {} void BLETextSensor::dump_config() { LOG_TEXT_SENSOR("", "BLE Text Sensor", this); - ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent()->address_str().c_str()); - ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Descriptor UUID : %s", this->descr_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Notifications : %s", YESNO(this->notify_)); + ESP_LOGCONFIG(TAG, + " MAC address : %s\n" + " Service UUID : %s\n" + " Characteristic UUID: %s\n" + " Descriptor UUID : %s\n" + " Notifications : %s", + this->parent()->address_str().c_str(), this->service_uuid_.to_string().c_str(), + this->char_uuid_.to_string().c_str(), this->descr_uuid_.to_string().c_str(), YESNO(this->notify_)); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/bluetooth_proxy/__init__.py b/esphome/components/bluetooth_proxy/__init__.py index 04ac9116c7..5c144cadcc 100644 --- a/esphome/components/bluetooth_proxy/__init__.py +++ b/esphome/components/bluetooth_proxy/__init__.py @@ -1,6 +1,7 @@ import esphome.codegen as cg -from esphome.components import esp32_ble_client, esp32_ble_tracker +from esphome.components import esp32_ble, esp32_ble_client, esp32_ble_tracker from esphome.components.esp32 import add_idf_sdkconfig_option +from esphome.components.esp32_ble import BTLoggers import esphome.config_validation as cv from esphome.const import CONF_ACTIVE, CONF_ID @@ -77,6 +78,9 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.L2CAP, BTLoggers.SMP) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp index 3c5c2bd438..44d434802c 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp @@ -75,7 +75,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga resp.data.reserve(param->read.value_len); // Use bulk insert instead of individual push_backs resp.data.insert(resp.data.end(), param->read.value, param->read.value + param->read.value_len); - this->proxy_->get_api_connection()->send_bluetooth_gatt_read_response(resp); + this->proxy_->get_api_connection()->send_message(resp); break; } case ESP_GATTC_WRITE_CHAR_EVT: @@ -89,7 +89,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga api::BluetoothGATTWriteResponse resp; resp.address = this->address_; resp.handle = param->write.handle; - this->proxy_->get_api_connection()->send_bluetooth_gatt_write_response(resp); + this->proxy_->get_api_connection()->send_message(resp); break; } case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: { @@ -103,7 +103,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga api::BluetoothGATTNotifyResponse resp; resp.address = this->address_; resp.handle = param->unreg_for_notify.handle; - this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_response(resp); + this->proxy_->get_api_connection()->send_message(resp); break; } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { @@ -116,7 +116,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga api::BluetoothGATTNotifyResponse resp; resp.address = this->address_; resp.handle = param->reg_for_notify.handle; - this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_response(resp); + this->proxy_->get_api_connection()->send_message(resp); break; } case ESP_GATTC_NOTIFY_EVT: { @@ -128,7 +128,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga resp.data.reserve(param->notify.value_len); // Use bulk insert instead of individual push_backs resp.data.insert(resp.data.end(), param->notify.value, param->notify.value + param->notify.value_len); - this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_data_response(resp); + this->proxy_->get_api_connection()->send_message(resp); break; } default: diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index d8b2111cb0..fbe2a3e67c 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -39,7 +39,7 @@ void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerSta resp.state = static_cast(state); resp.mode = this->parent_->get_scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE : api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE; - this->api_connection_->send_bluetooth_scanner_state_response(resp); + this->api_connection_->send_message(resp); } bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { @@ -58,7 +58,7 @@ static std::vector &get_batch_buffer() { return batch_buffer; } -bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) { +bool BluetoothProxy::parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) { if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_) return false; @@ -73,7 +73,7 @@ bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_p // Add new advertisements to the batch buffer for (size_t i = 0; i < count; i++) { - auto &result = advertisements[i]; + auto &result = scan_results[i]; uint8_t length = result.adv_data_len + result.scan_rsp_len; batch_buffer.emplace_back(); @@ -103,7 +103,7 @@ void BluetoothProxy::flush_pending_advertisements() { api::BluetoothLERawAdvertisementsResponse resp; resp.advertisements.swap(batch_buffer); - this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp); + this->api_connection_->send_message(resp); } void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) { @@ -141,14 +141,16 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi manufacturer_data.data.assign(data.data.begin(), data.data.end()); } - this->api_connection_->send_bluetooth_le_advertisement(resp); + this->api_connection_->send_message(resp); } void BluetoothProxy::dump_config() { ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); - ESP_LOGCONFIG(TAG, " Active: %s", YESNO(this->active_)); - ESP_LOGCONFIG(TAG, " Connections: %d", this->connections_.size()); - ESP_LOGCONFIG(TAG, " Raw advertisements: %s", YESNO(this->raw_advertisements_)); + ESP_LOGCONFIG(TAG, + " Active: %s\n" + " Connections: %d\n" + " Raw advertisements: %s", + YESNO(this->active_), this->connections_.size(), YESNO(this->raw_advertisements_)); } int BluetoothProxy::get_bluetooth_connections_free() { @@ -300,7 +302,7 @@ void BluetoothProxy::loop() { service_resp.characteristics.push_back(std::move(characteristic_resp)); } resp.services.push_back(std::move(service_resp)); - this->api_connection_->send_bluetooth_gatt_get_services_response(resp); + this->api_connection_->send_message(resp); } } } @@ -453,7 +455,7 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest call.success = ret == ESP_OK; call.error = ret; - this->api_connection_->send_bluetooth_device_clear_cache_response(call); + this->api_connection_->send_message(call); break; } @@ -577,7 +579,7 @@ void BluetoothProxy::send_device_connection(uint64_t address, bool connected, ui call.connected = connected; call.mtu = mtu; call.error = error; - this->api_connection_->send_bluetooth_device_connection_response(call); + this->api_connection_->send_message(call); } void BluetoothProxy::send_connections_free() { if (this->api_connection_ == nullptr) @@ -590,7 +592,7 @@ void BluetoothProxy::send_connections_free() { call.allocated.push_back(connection->address_); } } - this->api_connection_->send_bluetooth_connections_free_response(call); + this->api_connection_->send_message(call); } void BluetoothProxy::send_gatt_services_done(uint64_t address) { @@ -598,7 +600,7 @@ void BluetoothProxy::send_gatt_services_done(uint64_t address) { return; api::BluetoothGATTGetServicesDoneResponse call; call.address = address; - this->api_connection_->send_bluetooth_gatt_get_services_done_response(call); + this->api_connection_->send_message(call); } void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_t error) { @@ -608,7 +610,7 @@ void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_ call.address = address; call.handle = handle; call.error = error; - this->api_connection_->send_bluetooth_gatt_error_response(call); + this->api_connection_->send_message(call); } void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_t error) { @@ -617,7 +619,7 @@ void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_ call.paired = paired; call.error = error; - this->api_connection_->send_bluetooth_device_pairing_response(call); + this->api_connection_->send_message(call); } void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_err_t error) { @@ -626,7 +628,7 @@ void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_e call.success = success; call.error = error; - this->api_connection_->send_bluetooth_device_unpairing_response(call); + this->api_connection_->send_message(call); } void BluetoothProxy::bluetooth_scanner_set_mode(bool active) { diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index f75e73e796..16db0a0a11 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -52,7 +52,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com public: BluetoothProxy(); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; - bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) override; + bool parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) override; void dump_config() override; void setup() override; void loop() override; diff --git a/esphome/components/bme280_base/bme280_base.cpp b/esphome/components/bme280_base/bme280_base.cpp index 76e20836c7..d2524e5aac 100644 --- a/esphome/components/bme280_base/bme280_base.cpp +++ b/esphome/components/bme280_base/bme280_base.cpp @@ -88,14 +88,13 @@ const char *oversampling_to_str(BME280Oversampling oversampling) { // NOLINT } void BME280Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up BME280..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t chip_id = 0; // Mark as not failed before initializing. Some devices will turn off sensors to save on batteries // and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component. - if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) { - this->component_state_ &= ~COMPONENT_STATE_MASK; - this->component_state_ |= COMPONENT_STATE_CONSTRUCTION; + if (this->is_failed()) { + this->reset_to_construction_state(); } if (!this->read_byte(BME280_REGISTER_CHIPID, &chip_id)) { @@ -182,7 +181,7 @@ void BME280Component::dump_config() { ESP_LOGCONFIG(TAG, "BME280:"); switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Communication with BME280 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case WRONG_CHIP_ID: ESP_LOGE(TAG, "BME280 has wrong chip ID! Is it a BME280?"); @@ -207,7 +206,7 @@ inline uint8_t oversampling_to_time(BME280Oversampling over_sampling) { return ( void BME280Component::update() { // Enable sensor - ESP_LOGV(TAG, "Sending conversion request..."); + ESP_LOGV(TAG, "Sending conversion request"); uint8_t meas_value = 0; meas_value |= (this->temperature_oversampling_ & 0b111) << 5; meas_value |= (this->pressure_oversampling_ & 0b111) << 2; diff --git a/esphome/components/bme680/bme680.cpp b/esphome/components/bme680/bme680.cpp index 2b48f39e31..7e8f2f5a32 100644 --- a/esphome/components/bme680/bme680.cpp +++ b/esphome/components/bme680/bme680.cpp @@ -71,7 +71,7 @@ static const char *iir_filter_to_str(BME680IIRFilter filter) { } void BME680Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up BME680..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t chip_id; if (!this->read_byte(BME680_REGISTER_CHIPID, &chip_id) || chip_id != 0x61) { this->mark_failed(); @@ -215,7 +215,7 @@ void BME680Component::dump_config() { ESP_LOGCONFIG(TAG, "BME680:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with BME680 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } ESP_LOGCONFIG(TAG, " IIR Filter: %s", iir_filter_to_str(this->iir_filter_)); LOG_UPDATE_INTERVAL(this); @@ -307,7 +307,7 @@ void BME680Component::read_data_() { this->humidity_sensor_->publish_state(NAN); if (this->gas_resistance_sensor_ != nullptr) this->gas_resistance_sensor_->publish_state(NAN); - ESP_LOGW(TAG, "Communication with BME680 failed!"); + ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning(); return; } diff --git a/esphome/components/bme680/sensor.py b/esphome/components/bme680/sensor.py index 5937ac6ad8..abdf6d3969 100644 --- a/esphome/components/bme680/sensor.py +++ b/esphome/components/bme680/sensor.py @@ -13,7 +13,7 @@ from esphome.const import ( CONF_PRESSURE, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, - DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_ATMOSPHERIC_PRESSURE, DEVICE_CLASS_TEMPERATURE, ICON_GAS_CYLINDER, STATE_CLASS_MEASUREMENT, @@ -71,7 +71,7 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_PRESSURE): sensor.sensor_schema( unit_of_measurement=UNIT_HECTOPASCAL, accuracy_decimals=1, - device_class=DEVICE_CLASS_PRESSURE, + device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE, state_class=STATE_CLASS_MEASUREMENT, ).extend( { diff --git a/esphome/components/bme680_bsec/bme680_bsec.cpp b/esphome/components/bme680_bsec/bme680_bsec.cpp index 17dae35b5c..562d39e7b5 100644 --- a/esphome/components/bme680_bsec/bme680_bsec.cpp +++ b/esphome/components/bme680_bsec/bme680_bsec.cpp @@ -1,6 +1,6 @@ #include "bme680_bsec.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include namespace esphome { @@ -15,7 +15,7 @@ std::vector uint8_t BME680BSECComponent::work_buffer_[BSEC_MAX_WORKBUFFER_SIZE] = {0}; void BME680BSECComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up BME680(%s) via BSEC...", this->device_id_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->device_id_.c_str()); uint8_t new_idx = BME680BSECComponent::instances.size(); BME680BSECComponent::instances.push_back(this); @@ -159,11 +159,15 @@ void BME680BSECComponent::dump_config() { this->bme680_status_); } - ESP_LOGCONFIG(TAG, " Temperature Offset: %.2f", this->temperature_offset_); - ESP_LOGCONFIG(TAG, " IAQ Mode: %s", this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile"); - ESP_LOGCONFIG(TAG, " Supply Voltage: %sV", this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8"); - ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_)); - ESP_LOGCONFIG(TAG, " State Save Interval: %ims", this->state_save_interval_ms_); + ESP_LOGCONFIG(TAG, + " Temperature Offset: %.2f\n" + " IAQ Mode: %s\n" + " Supply Voltage: %sV\n" + " Sample Rate: %s\n" + " State Save Interval: %ims", + this->temperature_offset_, this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile", + this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8", + BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_), this->state_save_interval_ms_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->temperature_sample_rate_)); diff --git a/esphome/components/bme680_bsec/sensor.py b/esphome/components/bme680_bsec/sensor.py index 5107b0bfcd..8d3ae76e3f 100644 --- a/esphome/components/bme680_bsec/sensor.py +++ b/esphome/components/bme680_bsec/sensor.py @@ -15,6 +15,8 @@ from esphome.const import ( DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, ICON_GAS_CYLINDER, ICON_GAUGE, + ICON_THERMOMETER, + ICON_WATER_PERCENT, STATE_CLASS_MEASUREMENT, UNIT_CELSIUS, UNIT_HECTOPASCAL, @@ -27,11 +29,11 @@ from . import CONF_BME680_BSEC_ID, SAMPLE_RATE_OPTIONS, BME680BSECComponent DEPENDENCIES = ["bme680_bsec"] -CONF_IAQ = "iaq" -CONF_CO2_EQUIVALENT = "co2_equivalent" CONF_BREATH_VOC_EQUIVALENT = "breath_voc_equivalent" -UNIT_IAQ = "IAQ" +CONF_CO2_EQUIVALENT = "co2_equivalent" +CONF_IAQ = "iaq" ICON_ACCURACY = "mdi:checkbox-marked-circle-outline" +UNIT_IAQ = "IAQ" TYPES = [ CONF_TEMPERATURE, @@ -49,6 +51,7 @@ CONFIG_SCHEMA = cv.Schema( cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent), cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, accuracy_decimals=1, device_class=DEVICE_CLASS_TEMPERATURE, state_class=STATE_CLASS_MEASUREMENT, @@ -65,6 +68,7 @@ CONFIG_SCHEMA = cv.Schema( ), cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( unit_of_measurement=UNIT_PERCENT, + icon=ICON_WATER_PERCENT, accuracy_decimals=1, device_class=DEVICE_CLASS_HUMIDITY, state_class=STATE_CLASS_MEASUREMENT, diff --git a/esphome/components/bme68x_bsec2/__init__.py b/esphome/components/bme68x_bsec2/__init__.py index d6dbb52f18..f4235b31b4 100644 --- a/esphome/components/bme68x_bsec2/__init__.py +++ b/esphome/components/bme68x_bsec2/__init__.py @@ -16,7 +16,7 @@ CODEOWNERS = ["@neffs", "@kbx81"] DOMAIN = "bme68x_bsec2" -BSEC2_LIBRARY_VERSION = "v1.8.2610" +BSEC2_LIBRARY_VERSION = "1.10.2610" CONF_ALGORITHM_OUTPUT = "algorithm_output" CONF_BME68X_BSEC2_ID = "bme68x_bsec2_id" @@ -145,7 +145,6 @@ CONFIG_SCHEMA_BASE = ( ): cv.positive_time_period_minutes, }, ) - .add_extra(cv.only_with_arduino) .add_extra(validate_bme68x) .add_extra(download_bme68x_blob) ) @@ -179,11 +178,13 @@ async def to_code_base(config): bsec2_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) cg.add(var.set_bsec2_configuration(bsec2_arr, len(rhs))) - # Although this component does not use SPI, the BSEC2 library requires the SPI library - cg.add_library("SPI", None) + # Although this component does not use SPI, the BSEC2 Arduino library requires the SPI library + if core.CORE.using_arduino: + cg.add_library("SPI", None) cg.add_library( "BME68x Sensor library", - "1.1.40407", + "1.3.40408", + "https://github.com/boschsensortec/Bosch-BME68x-Library", ) cg.add_library( "BSEC2 Software Library", diff --git a/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp b/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp index f83f20f1a5..a23711c4ca 100644 --- a/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +++ b/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp @@ -1,4 +1,5 @@ #include "esphome/core/defines.h" +#include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" @@ -20,7 +21,7 @@ static const char *const TAG = "bme68x_bsec2.sensor"; static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"}; void BME68xBSEC2Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up BME68X via BSEC2..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->bsec_status_ = bsec_init_m(&this->bsec_instance_); if (this->bsec_status_ != BSEC_OK) { @@ -57,13 +58,13 @@ void BME68xBSEC2Component::setup() { } void BME68xBSEC2Component::dump_config() { - ESP_LOGCONFIG(TAG, "BME68X via BSEC2:"); - - ESP_LOGCONFIG(TAG, " BSEC2 version: %d.%d.%d.%d", this->version_.major, this->version_.minor, - this->version_.major_bugfix, this->version_.minor_bugfix); - - ESP_LOGCONFIG(TAG, " BSEC2 configuration blob:"); - ESP_LOGCONFIG(TAG, " Configured: %s", YESNO(this->bsec2_blob_configured_)); + ESP_LOGCONFIG(TAG, + "BME68X via BSEC2:\n" + " BSEC2 version: %d.%d.%d.%d\n" + " BSEC2 configuration blob:\n" + " Configured: %s", + this->version_.major, this->version_.minor, this->version_.major_bugfix, this->version_.minor_bugfix, + YESNO(this->bsec2_blob_configured_)); if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) { ESP_LOGCONFIG(TAG, " Size: %" PRIu32, this->bsec2_configuration_length_); } @@ -76,11 +77,14 @@ void BME68xBSEC2Component::dump_config() { if (this->algorithm_output_ != ALGORITHM_OUTPUT_IAQ) { ESP_LOGCONFIG(TAG, " Algorithm output: %s", BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(this->algorithm_output_)); } - ESP_LOGCONFIG(TAG, " Operating age: %s", BME68X_BSEC2_OPERATING_AGE_LOG(this->operating_age_)); - ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->sample_rate_)); - ESP_LOGCONFIG(TAG, " Voltage: %s", BME68X_BSEC2_VOLTAGE_LOG(this->voltage_)); - ESP_LOGCONFIG(TAG, " State save interval: %ims", this->state_save_interval_ms_); - ESP_LOGCONFIG(TAG, " Temperature offset: %.2f", this->temperature_offset_); + ESP_LOGCONFIG(TAG, + " Operating age: %s\n" + " Sample rate: %s\n" + " Voltage: %s\n" + " State save interval: %ims\n" + " Temperature offset: %.2f", + BME68X_BSEC2_OPERATING_AGE_LOG(this->operating_age_), BME68X_BSEC2_SAMPLE_RATE_LOG(this->sample_rate_), + BME68X_BSEC2_VOLTAGE_LOG(this->voltage_), this->state_save_interval_ms_, this->temperature_offset_); #ifdef USE_SENSOR LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); diff --git a/esphome/components/bme68x_bsec2/sensor.py b/esphome/components/bme68x_bsec2/sensor.py index 419f47b248..c7dca437d7 100644 --- a/esphome/components/bme68x_bsec2/sensor.py +++ b/esphome/components/bme68x_bsec2/sensor.py @@ -9,8 +9,10 @@ from esphome.const import ( CONF_SAMPLE_RATE, CONF_TEMPERATURE, DEVICE_CLASS_ATMOSPHERIC_PRESSURE, + DEVICE_CLASS_CARBON_DIOXIDE, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, ICON_GAS_CYLINDER, ICON_GAUGE, ICON_THERMOMETER, @@ -32,7 +34,6 @@ CONF_CO2_EQUIVALENT = "co2_equivalent" CONF_IAQ = "iaq" CONF_IAQ_STATIC = "iaq_static" ICON_ACCURACY = "mdi:checkbox-marked-circle-outline" -ICON_TEST_TUBE = "mdi:test-tube" UNIT_IAQ = "IAQ" TYPES = [ @@ -61,7 +62,6 @@ CONFIG_SCHEMA = cv.Schema( ), cv.Optional(CONF_PRESSURE): sensor.sensor_schema( unit_of_measurement=UNIT_HECTOPASCAL, - icon=ICON_GAUGE, accuracy_decimals=1, device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE, state_class=STATE_CLASS_MEASUREMENT, @@ -102,14 +102,14 @@ CONFIG_SCHEMA = cv.Schema( ), cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema( unit_of_measurement=UNIT_PARTS_PER_MILLION, - icon=ICON_TEST_TUBE, accuracy_decimals=1, + device_class=DEVICE_CLASS_CARBON_DIOXIDE, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_BREATH_VOC_EQUIVALENT): sensor.sensor_schema( unit_of_measurement=UNIT_PARTS_PER_MILLION, - icon=ICON_TEST_TUBE, accuracy_decimals=1, + device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, state_class=STATE_CLASS_MEASUREMENT, ), } diff --git a/esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp b/esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp index 874c8bf388..50eaf33add 100644 --- a/esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp +++ b/esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp @@ -1,4 +1,5 @@ #include "esphome/core/defines.h" +#include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" diff --git a/esphome/components/bmi160/bmi160.cpp b/esphome/components/bmi160/bmi160.cpp index 69b4694345..aca42f1b52 100644 --- a/esphome/components/bmi160/bmi160.cpp +++ b/esphome/components/bmi160/bmi160.cpp @@ -119,44 +119,44 @@ const float GRAVITY_EARTH = 9.80665f; void BMI160Component::internal_setup_(int stage) { switch (stage) { case 0: - ESP_LOGCONFIG(TAG, "Setting up BMI160..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t chipid; if (!this->read_byte(BMI160_REGISTER_CHIPID, &chipid) || (chipid != 0b11010001)) { this->mark_failed(); return; } - ESP_LOGV(TAG, " Bringing accelerometer out of sleep..."); + ESP_LOGV(TAG, " Bringing accelerometer out of sleep"); if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::ACCL_SET_PMU_MODE | (uint8_t) AcclPmuMode::NORMAL)) { this->mark_failed(); return; } - ESP_LOGV(TAG, " Waiting for accelerometer to wake up..."); + ESP_LOGV(TAG, " Waiting for accelerometer to wake up"); // need to wait (max delay in datasheet) because we can't send commands while another is in progress // min 5ms, 10ms this->set_timeout(10, [this]() { this->internal_setup_(1); }); break; case 1: - ESP_LOGV(TAG, " Bringing gyroscope out of sleep..."); + ESP_LOGV(TAG, " Bringing gyroscope out of sleep"); if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::GYRO_SET_PMU_MODE | (uint8_t) GyroPmuMode::NORMAL)) { this->mark_failed(); return; } - ESP_LOGV(TAG, " Waiting for gyroscope to wake up..."); + ESP_LOGV(TAG, " Waiting for gyroscope to wake up"); // wait between 51 & 81ms, doing 100 to be safe this->set_timeout(10, [this]() { this->internal_setup_(2); }); break; case 2: - ESP_LOGV(TAG, " Setting up Gyro Config..."); + ESP_LOGV(TAG, " Setting up Gyro Config"); uint8_t gyro_config = (uint8_t) GyroBandwidth::OSR4 | (uint8_t) GyroOuputDataRate::HZ_25; ESP_LOGV(TAG, " Output gyro_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_config)); if (!this->write_byte(BMI160_REGISTER_GYRO_CONFIG, gyro_config)) { this->mark_failed(); return; } - ESP_LOGV(TAG, " Setting up Gyro Range..."); + ESP_LOGV(TAG, " Setting up Gyro Range"); uint8_t gyro_range = (uint8_t) GyroRange::RANGE_2000_DPS; ESP_LOGV(TAG, " Output gyro_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_range)); if (!this->write_byte(BMI160_REGISTER_GYRO_RANGE, gyro_range)) { @@ -164,7 +164,7 @@ void BMI160Component::internal_setup_(int stage) { return; } - ESP_LOGV(TAG, " Setting up Accel Config..."); + ESP_LOGV(TAG, " Setting up Accel Config"); uint8_t accel_config = (uint8_t) AcclFilterMode::PERF | (uint8_t) AcclBandwidth::RES_AVG16 | (uint8_t) AccelOutputDataRate::HZ_25; ESP_LOGV(TAG, " Output accel_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_config)); @@ -172,7 +172,7 @@ void BMI160Component::internal_setup_(int stage) { this->mark_failed(); return; } - ESP_LOGV(TAG, " Setting up Accel Range..."); + ESP_LOGV(TAG, " Setting up Accel Range"); uint8_t accel_range = (uint8_t) AccelRange::RANGE_16G; ESP_LOGV(TAG, " Output accel_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_range)); if (!this->write_byte(BMI160_REGISTER_ACCEL_RANGE, accel_range)) { @@ -189,7 +189,7 @@ void BMI160Component::dump_config() { ESP_LOGCONFIG(TAG, "BMI160:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with BMI160 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Acceleration X", this->accel_x_sensor_); @@ -219,7 +219,7 @@ void BMI160Component::update() { return; } - ESP_LOGV(TAG, " Updating BMI160..."); + ESP_LOGV(TAG, " Updating BMI160"); int16_t data[6]; if (this->read_le_int16_(BMI160_REGISTER_DATA_GYRO_X_LSB, data, 6) != i2c::ERROR_OK) { this->status_set_warning(); diff --git a/esphome/components/bmp085/bmp085.cpp b/esphome/components/bmp085/bmp085.cpp index caf2264390..94dc61891b 100644 --- a/esphome/components/bmp085/bmp085.cpp +++ b/esphome/components/bmp085/bmp085.cpp @@ -20,7 +20,7 @@ void BMP085Component::update() { this->set_timeout("temperature", 5, [this]() { this->read_temperature_(); }); } void BMP085Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up BMP085..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t data[22]; if (!this->read_bytes(BMP085_REGISTER_AC1_H, data, 22)) { this->mark_failed(); @@ -129,7 +129,7 @@ void BMP085Component::read_pressure_() { this->status_clear_warning(); } bool BMP085Component::set_mode_(uint8_t mode) { - ESP_LOGV(TAG, "Setting mode to 0x%02X...", mode); + ESP_LOGV(TAG, "Setting mode to 0x%02X", mode); return this->write_byte(BMP085_REGISTER_CONTROL, mode); } float BMP085Component::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/bmp280_base/bmp280_base.cpp b/esphome/components/bmp280_base/bmp280_base.cpp index f94456f6e6..94b8bd6540 100644 --- a/esphome/components/bmp280_base/bmp280_base.cpp +++ b/esphome/components/bmp280_base/bmp280_base.cpp @@ -57,7 +57,7 @@ static const char *iir_filter_to_str(BMP280IIRFilter filter) { } void BMP280Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up BMP280..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t chip_id = 0; // Read the chip id twice, to work around a bug where the first read is 0. @@ -132,7 +132,7 @@ void BMP280Component::dump_config() { ESP_LOGCONFIG(TAG, "BMP280:"); switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Communication with BMP280 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case WRONG_CHIP_ID: ESP_LOGE(TAG, "BMP280 has wrong chip ID! Is it a BME280?"); @@ -155,7 +155,7 @@ inline uint8_t oversampling_to_time(BMP280Oversampling over_sampling) { return ( void BMP280Component::update() { // Enable sensor - ESP_LOGV(TAG, "Sending conversion request..."); + ESP_LOGV(TAG, "Sending conversion request"); uint8_t meas_value = 0; meas_value |= (this->temperature_oversampling_ & 0b111) << 5; meas_value |= (this->pressure_oversampling_ & 0b111) << 2; diff --git a/esphome/components/bmp3xx_base/bmp3xx_base.cpp b/esphome/components/bmp3xx_base/bmp3xx_base.cpp index 75b6812f81..979f354cb2 100644 --- a/esphome/components/bmp3xx_base/bmp3xx_base.cpp +++ b/esphome/components/bmp3xx_base/bmp3xx_base.cpp @@ -70,10 +70,10 @@ static const LogString *iir_filter_to_str(IIRFilter filter) { void BMP3XXComponent::setup() { this->error_code_ = NONE; - ESP_LOGCONFIG(TAG, "Setting up BMP3XX..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Call the Device base class "initialise" function if (!reset()) { - ESP_LOGE(TAG, "Failed to reset BMP3XX..."); + ESP_LOGE(TAG, "Failed to reset"); this->error_code_ = ERROR_SENSOR_RESET; this->mark_failed(); } @@ -148,25 +148,25 @@ void BMP3XXComponent::setup() { } void BMP3XXComponent::dump_config() { - ESP_LOGCONFIG(TAG, "BMP3XX:"); - ESP_LOGCONFIG(TAG, " Type: %s (0x%X)", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg); + ESP_LOGCONFIG(TAG, + "BMP3XX:\n" + " Type: %s (0x%X)", + LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg); switch (this->error_code_) { case NONE: break; case ERROR_COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Communication with BMP3XX failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case ERROR_WRONG_CHIP_ID: - ESP_LOGE( - TAG, - "BMP3XX has wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390", - this->chip_id_.reg); + ESP_LOGE(TAG, "Wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390", + this->chip_id_.reg); break; case ERROR_SENSOR_RESET: - ESP_LOGE(TAG, "BMP3XX failed to reset"); + ESP_LOGE(TAG, "Failed to reset"); break; default: - ESP_LOGE(TAG, "BMP3XX error code %d", (int) this->error_code_); + ESP_LOGE(TAG, "Error code %d", (int) this->error_code_); break; } ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_filter_))); @@ -186,7 +186,7 @@ inline uint8_t oversampling_to_time(Oversampling over_sampling) { return (1 << u void BMP3XXComponent::update() { // Enable sensor - ESP_LOGV(TAG, "Sending conversion request..."); + ESP_LOGV(TAG, "Sending conversion request"); float meas_time = 1.0f; // Ref: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf 3.9.2 meas_time += 2.02f * oversampling_to_time(this->temperature_oversampling_) + 0.163f; @@ -296,7 +296,7 @@ bool BMP3XXComponent::get_pressure(float &pressure) { bool BMP3XXComponent::get_measurements(float &temperature, float &pressure) { // Check if a measurement is ready if (!data_ready()) { - ESP_LOGD(TAG, "BMP3XX Get measurement - data not ready skipping update"); + ESP_LOGD(TAG, "Get measurement - data not ready skipping update"); return false; } diff --git a/esphome/components/bmp581/bmp581.cpp b/esphome/components/bmp581/bmp581.cpp index 0308da0bcb..2204a6af2e 100644 --- a/esphome/components/bmp581/bmp581.cpp +++ b/esphome/components/bmp581/bmp581.cpp @@ -72,22 +72,22 @@ void BMP581Component::dump_config() { case NONE: break; case ERROR_COMMUNICATION_FAILED: - ESP_LOGE(TAG, " Communication with BMP581 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case ERROR_WRONG_CHIP_ID: - ESP_LOGE(TAG, " BMP581 has wrong chip ID - please verify you are using a BMP 581"); + ESP_LOGE(TAG, "Unknown chip ID"); break; case ERROR_SENSOR_RESET: - ESP_LOGE(TAG, " BMP581 failed to reset"); + ESP_LOGE(TAG, "Reset failed"); break; case ERROR_SENSOR_STATUS: - ESP_LOGE(TAG, " BMP581 sensor status failed, there were NVM problems"); + ESP_LOGE(TAG, "Get status failed"); break; case ERROR_PRIME_IIR_FAILED: - ESP_LOGE(TAG, " BMP581's IIR Filter failed to prime with an initial measurement"); + ESP_LOGE(TAG, "IIR Filter failed to prime with initial measurement"); break; default: - ESP_LOGE(TAG, " BMP581 error code %d", (int) this->error_code_); + ESP_LOGE(TAG, "Error %d", (int) this->error_code_); break; } @@ -98,14 +98,20 @@ void BMP581Component::dump_config() { if (this->temperature_sensor_) { LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); - ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_temperature_level_))); - ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_))); + ESP_LOGCONFIG(TAG, + " IIR Filter: %s\n" + " Oversampling: %s", + LOG_STR_ARG(iir_filter_to_str(this->iir_temperature_level_)), + LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_))); } if (this->pressure_sensor_) { LOG_SENSOR(" ", "Pressure", this->pressure_sensor_); - ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_pressure_level_))); - ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_))); + ESP_LOGCONFIG(TAG, + " IIR Filter: %s\n" + " Oversampling: %s", + LOG_STR_ARG(iir_filter_to_str(this->iir_pressure_level_)), + LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_))); } } @@ -122,7 +128,7 @@ void BMP581Component::setup() { */ this->error_code_ = NONE; - ESP_LOGCONFIG(TAG, "Setting up BMP581..."); + ESP_LOGCONFIG(TAG, "Running setup"); //////////////////// // 1) Soft reboot // @@ -130,7 +136,7 @@ void BMP581Component::setup() { // Power-On-Reboot bit is asserted if sensor successfully reset if (!this->reset_()) { - ESP_LOGE(TAG, "BMP581 failed to reset"); + ESP_LOGE(TAG, "Reset failed"); this->error_code_ = ERROR_SENSOR_RESET; this->mark_failed(); @@ -146,7 +152,7 @@ void BMP581Component::setup() { // read chip id from sensor if (!this->read_byte(BMP581_CHIP_ID, &chip_id)) { - ESP_LOGE(TAG, "Failed to read chip id"); + ESP_LOGE(TAG, "Read chip ID failed"); this->error_code_ = ERROR_COMMUNICATION_FAILED; this->mark_failed(); @@ -156,7 +162,7 @@ void BMP581Component::setup() { // verify id if (chip_id != BMP581_ASIC_ID) { - ESP_LOGE(TAG, "Unknown chip ID, is this a BMP581?"); + ESP_LOGE(TAG, "Unknown chip ID"); this->error_code_ = ERROR_WRONG_CHIP_ID; this->mark_failed(); @@ -179,7 +185,7 @@ void BMP581Component::setup() { // verify status_nvm_rdy bit (it is asserted if boot was successful) if (!(this->status_.bit.status_nvm_rdy)) { - ESP_LOGE(TAG, "NVM not ready after boot"); + ESP_LOGE(TAG, "NVM not ready"); this->error_code_ = ERROR_SENSOR_STATUS; this->mark_failed(); @@ -189,7 +195,7 @@ void BMP581Component::setup() { // verify status_nvm_err bit (it is asserted if an error is detected) if (this->status_.bit.status_nvm_err) { - ESP_LOGE(TAG, "NVM error detected on boot"); + ESP_LOGE(TAG, "NVM error detected"); this->error_code_ = ERROR_SENSOR_STATUS; this->mark_failed(); @@ -254,7 +260,7 @@ void BMP581Component::setup() { } if (!this->prime_iir_filter_()) { - ESP_LOGE(TAG, "Failed to prime the IIR filter with an intiial measurement"); + ESP_LOGE(TAG, "Failed to prime the IIR filter with an initial measurement"); this->error_code_ = ERROR_PRIME_IIR_FAILED; this->mark_failed(); @@ -286,10 +292,10 @@ void BMP581Component::update() { // 1) Request a measurement // ////////////////////////////// - ESP_LOGVV(TAG, "Requesting a measurement from sensor"); + ESP_LOGVV(TAG, "Requesting measurement"); if (!this->start_measurement_()) { - ESP_LOGW(TAG, "Failed to request forced measurement of sensor"); + ESP_LOGW(TAG, "Requesting forced measurement failed"); this->status_set_warning(); return; @@ -299,7 +305,7 @@ void BMP581Component::update() { // 2) Wait for measurement to finish (based on oversampling rates) // ////////////////////////////////////////////////////////////////////// - ESP_LOGVV(TAG, "Measurement is expected to take %d ms to complete", this->conversion_time_); + ESP_LOGVV(TAG, "Measurement should take %d ms", this->conversion_time_); this->set_timeout("measurement", this->conversion_time_, [this]() { float temperature = 0.0; @@ -311,14 +317,14 @@ void BMP581Component::update() { if (this->pressure_sensor_) { if (!this->read_temperature_and_pressure_(temperature, pressure)) { - ESP_LOGW(TAG, "Failed to read temperature and pressure measurements, skipping update"); + ESP_LOGW(TAG, "Failed to read temperature and pressure; skipping update"); this->status_set_warning(); return; } } else { if (!this->read_temperature_(temperature)) { - ESP_LOGW(TAG, "Failed to read temperature measurement, skipping update"); + ESP_LOGW(TAG, "Failed to read temperature; skipping update"); this->status_set_warning(); return; @@ -349,7 +355,7 @@ bool BMP581Component::check_data_readiness_() { // - returns data readiness state if (this->odr_config_.bit.pwr_mode == STANDBY_MODE) { - ESP_LOGD(TAG, "Data is not ready, sensor is in standby mode"); + ESP_LOGD(TAG, "Data not ready, sensor is in standby mode"); return false; } @@ -443,7 +449,7 @@ bool BMP581Component::read_temperature_(float &temperature) { // - the measured temperature (in degrees Celsius) if (!this->check_data_readiness_()) { - ESP_LOGW(TAG, "Data from sensor isn't ready, skipping this update"); + ESP_LOGW(TAG, "Data not ready, skipping this update"); this->status_set_warning(); return false; @@ -451,7 +457,7 @@ bool BMP581Component::read_temperature_(float &temperature) { uint8_t data[3]; if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 3)) { - ESP_LOGW(TAG, "Failed to read sensor's measurement data"); + ESP_LOGW(TAG, "Failed to read measurement"); this->status_set_warning(); return false; @@ -472,7 +478,7 @@ bool BMP581Component::read_temperature_and_pressure_(float &temperature, float & // - the measured pressure (in Pa) if (!this->check_data_readiness_()) { - ESP_LOGW(TAG, "Data from sensor isn't ready, skipping this update"); + ESP_LOGW(TAG, "Data not ready, skipping this update"); this->status_set_warning(); return false; @@ -480,7 +486,7 @@ bool BMP581Component::read_temperature_and_pressure_(float &temperature, float & uint8_t data[6]; if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 6)) { - ESP_LOGW(TAG, "Failed to read sensor's measurement data"); + ESP_LOGW(TAG, "Failed to read measurement"); this->status_set_warning(); return false; diff --git a/esphome/components/bp1658cj/bp1658cj.cpp b/esphome/components/bp1658cj/bp1658cj.cpp index 4b74cc85f5..b502a738cd 100644 --- a/esphome/components/bp1658cj/bp1658cj.cpp +++ b/esphome/components/bp1658cj/bp1658cj.cpp @@ -15,7 +15,7 @@ static const uint8_t BP1658CJ_ADDR_START_5CH = 0x30; static const uint8_t BP1658CJ_DELAY = 2; void BP1658CJ::setup() { - ESP_LOGCONFIG(TAG, "Setting up BP1658CJ Output Component..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->data_pin_->setup(); this->data_pin_->digital_write(false); this->clock_pin_->setup(); @@ -26,8 +26,10 @@ void BP1658CJ::dump_config() { ESP_LOGCONFIG(TAG, "BP1658CJ:"); LOG_PIN(" Data Pin: ", this->data_pin_); LOG_PIN(" Clock Pin: ", this->clock_pin_); - ESP_LOGCONFIG(TAG, " Color Channels Max Power: %u", this->max_power_color_channels_); - ESP_LOGCONFIG(TAG, " White Channels Max Power: %u", this->max_power_white_channels_); + ESP_LOGCONFIG(TAG, + " Color Channels Max Power: %u\n" + " White Channels Max Power: %u", + this->max_power_color_channels_, this->max_power_white_channels_); } void BP1658CJ::loop() { diff --git a/esphome/components/bp5758d/bp5758d.cpp b/esphome/components/bp5758d/bp5758d.cpp index 87c4165275..797ddd919e 100644 --- a/esphome/components/bp5758d/bp5758d.cpp +++ b/esphome/components/bp5758d/bp5758d.cpp @@ -20,7 +20,7 @@ static const uint8_t BP5758D_ALL_DATA_CHANNEL_ENABLEMENT = 0b00011111; static const uint8_t BP5758D_DELAY = 2; void BP5758D::setup() { - ESP_LOGCONFIG(TAG, "Setting up BP5758D Output Component..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->data_pin_->setup(); this->data_pin_->digital_write(false); delayMicroseconds(BP5758D_DELAY); diff --git a/esphome/components/button/__init__.py b/esphome/components/button/__init__.py index b68334dd98..892bf62f3a 100644 --- a/esphome/components/button/__init__.py +++ b/esphome/components/button/__init__.py @@ -108,6 +108,7 @@ async def register_button(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_button(var)) + CORE.register_platform_component("button", var) await setup_button_core_(var, config) diff --git a/esphome/components/canbus/canbus.cpp b/esphome/components/canbus/canbus.cpp index 3b86f209cd..d08558037e 100644 --- a/esphome/components/canbus/canbus.cpp +++ b/esphome/components/canbus/canbus.cpp @@ -7,7 +7,7 @@ namespace canbus { static const char *const TAG = "canbus"; void Canbus::setup() { - ESP_LOGCONFIG(TAG, "Setting up Canbus..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->setup_internal()) { ESP_LOGE(TAG, "setup error!"); this->mark_failed(); diff --git a/esphome/components/cap1188/cap1188.cpp b/esphome/components/cap1188/cap1188.cpp index 10d8325537..af167deb99 100644 --- a/esphome/components/cap1188/cap1188.cpp +++ b/esphome/components/cap1188/cap1188.cpp @@ -8,7 +8,7 @@ namespace cap1188 { static const char *const TAG = "cap1188"; void CAP1188Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up CAP1188..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Reset device using the reset pin if (this->reset_pin_ != nullptr) { @@ -52,9 +52,11 @@ void CAP1188Component::dump_config() { ESP_LOGCONFIG(TAG, "CAP1188:"); LOG_I2C_DEVICE(this); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " Product ID: 0x%x", this->cap1188_product_id_); - ESP_LOGCONFIG(TAG, " Manufacture ID: 0x%x", this->cap1188_manufacture_id_); - ESP_LOGCONFIG(TAG, " Revision ID: 0x%x", this->cap1188_revision_); + ESP_LOGCONFIG(TAG, + " Product ID: 0x%x\n" + " Manufacture ID: 0x%x\n" + " Revision ID: 0x%x", + this->cap1188_product_id_, this->cap1188_manufacture_id_, this->cap1188_revision_); switch (this->error_code_) { case COMMUNICATION_FAILED: diff --git a/esphome/components/captive_portal/captive_portal.cpp b/esphome/components/captive_portal/captive_portal.cpp index d1960e9a93..31e6c51f0f 100644 --- a/esphome/components/captive_portal/captive_portal.cpp +++ b/esphome/components/captive_portal/captive_portal.cpp @@ -29,7 +29,7 @@ void CaptivePortal::handle_config(AsyncWebServerRequest *request) { void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) { std::string ssid = request->arg("ssid").c_str(); std::string psk = request->arg("psk").c_str(); - ESP_LOGI(TAG, "Captive Portal Requested WiFi Settings Change:"); + ESP_LOGI(TAG, "Requested WiFi Settings Change:"); ESP_LOGI(TAG, " SSID='%s'", ssid.c_str()); ESP_LOGI(TAG, " Password=" LOG_SECRET("'%s'"), psk.c_str()); wifi::global_wifi_component->save_wifi_sta(ssid, psk); diff --git a/esphome/components/ccs811/ccs811.cpp b/esphome/components/ccs811/ccs811.cpp index f1dadf673a..cecb92b3df 100644 --- a/esphome/components/ccs811/ccs811.cpp +++ b/esphome/components/ccs811/ccs811.cpp @@ -1,6 +1,7 @@ #include "ccs811.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ccs811 { @@ -163,7 +164,7 @@ void CCS811Component::dump_config() { if (this->is_failed()) { switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGW(TAG, "Communication failed! Is the sensor connected?"); + ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); break; case INVALID_ID: ESP_LOGW(TAG, "Sensor reported an invalid ID. Is this a CCS811?"); diff --git a/esphome/components/cd74hc4067/cd74hc4067.cpp b/esphome/components/cd74hc4067/cd74hc4067.cpp index f7629df3ca..3c7b9038d7 100644 --- a/esphome/components/cd74hc4067/cd74hc4067.cpp +++ b/esphome/components/cd74hc4067/cd74hc4067.cpp @@ -10,7 +10,7 @@ static const char *const TAG = "cd74hc4067"; float CD74HC4067Component::get_setup_priority() const { return setup_priority::DATA; } void CD74HC4067Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up CD74HC4067..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->pin_s0_->setup(); this->pin_s1_->setup(); diff --git a/esphome/components/ch422g/ch422g.cpp b/esphome/components/ch422g/ch422g.cpp index 0db179d99e..325c56e470 100644 --- a/esphome/components/ch422g/ch422g.cpp +++ b/esphome/components/ch422g/ch422g.cpp @@ -14,7 +14,7 @@ static const uint8_t CH422G_REG_OUT_UPPER = 0x23; // write reg for output bit static const char *const TAG = "ch422g"; void CH422GComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up CH422G..."); + ESP_LOGCONFIG(TAG, "Running setup"); // set outputs before mode this->write_outputs_(); // Set mode and check for errors @@ -37,7 +37,7 @@ void CH422GComponent::dump_config() { ESP_LOGCONFIG(TAG, "CH422G:"); LOG_I2C_DEVICE(this) if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with CH422G failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } diff --git a/esphome/components/chsc6x/chsc6x_touchscreen.cpp b/esphome/components/chsc6x/chsc6x_touchscreen.cpp index 5ad4329718..524fa1eb36 100644 --- a/esphome/components/chsc6x/chsc6x_touchscreen.cpp +++ b/esphome/components/chsc6x/chsc6x_touchscreen.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace chsc6x { void CHSC6XTouchscreen::setup() { - ESP_LOGCONFIG(TAG, "Setting up CHSC6X Touchscreen..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (this->interrupt_pin_ != nullptr) { this->interrupt_pin_->setup(); this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE); @@ -38,9 +38,11 @@ void CHSC6XTouchscreen::dump_config() { ESP_LOGCONFIG(TAG, "CHSC6X Touchscreen:"); LOG_I2C_DEVICE(this); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); - ESP_LOGCONFIG(TAG, " Touch timeout: %d", this->touch_timeout_); - ESP_LOGCONFIG(TAG, " x_raw_max_: %d", this->x_raw_max_); - ESP_LOGCONFIG(TAG, " y_raw_max_: %d", this->y_raw_max_); + ESP_LOGCONFIG(TAG, + " Touch timeout: %d\n" + " x_raw_max_: %d\n" + " y_raw_max_: %d", + this->touch_timeout_, this->x_raw_max_, this->y_raw_max_); } } // namespace chsc6x diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index 7007dc13af..52938a17d0 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -443,6 +443,7 @@ async def register_climate(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_climate(var)) + CORE.register_platform_component("climate", var) await setup_climate_core_(var, config) diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index bc8d932089..edebc0de69 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -569,17 +569,22 @@ bool Climate::set_custom_preset_(const std::string &preset) { void Climate::dump_traits_(const char *tag) { auto traits = this->get_traits(); ESP_LOGCONFIG(tag, "ClimateTraits:"); - ESP_LOGCONFIG(tag, " [x] Visual settings:"); - ESP_LOGCONFIG(tag, " - Min temperature: %.1f", traits.get_visual_min_temperature()); - ESP_LOGCONFIG(tag, " - Max temperature: %.1f", traits.get_visual_max_temperature()); - ESP_LOGCONFIG(tag, " - Temperature step:"); - ESP_LOGCONFIG(tag, " Target: %.1f", traits.get_visual_target_temperature_step()); + ESP_LOGCONFIG(tag, + " [x] Visual settings:\n" + " - Min temperature: %.1f\n" + " - Max temperature: %.1f\n" + " - Temperature step:\n" + " Target: %.1f", + traits.get_visual_min_temperature(), traits.get_visual_max_temperature(), + traits.get_visual_target_temperature_step()); if (traits.get_supports_current_temperature()) { ESP_LOGCONFIG(tag, " Current: %.1f", traits.get_visual_current_temperature_step()); } if (traits.get_supports_target_humidity() || traits.get_supports_current_humidity()) { - ESP_LOGCONFIG(tag, " - Min humidity: %.0f", traits.get_visual_min_humidity()); - ESP_LOGCONFIG(tag, " - Max humidity: %.0f", traits.get_visual_max_humidity()); + ESP_LOGCONFIG(tag, + " - Min humidity: %.0f\n" + " - Max humidity: %.0f", + traits.get_visual_min_humidity(), traits.get_visual_max_humidity()); } if (traits.get_supports_two_point_target_temperature()) { ESP_LOGCONFIG(tag, " [x] Supports two-point target temperature"); diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h index d81702fb0c..b31a2eedf6 100644 --- a/esphome/components/climate/climate.h +++ b/esphome/components/climate/climate.h @@ -3,8 +3,8 @@ #include "esphome/core/component.h" #include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" -#include "esphome/core/preferences.h" #include "esphome/core/log.h" +#include "esphome/core/preferences.h" #include "climate_mode.h" #include "climate_traits.h" diff --git a/esphome/components/climate_ir/climate_ir.cpp b/esphome/components/climate_ir/climate_ir.cpp index 8175383627..dc8117f6ae 100644 --- a/esphome/components/climate_ir/climate_ir.cpp +++ b/esphome/components/climate_ir/climate_ir.cpp @@ -75,10 +75,13 @@ void ClimateIR::control(const climate::ClimateCall &call) { } void ClimateIR::dump_config() { LOG_CLIMATE("", "IR Climate", this); - ESP_LOGCONFIG(TAG, " Min. Temperature: %.1f°C", this->minimum_temperature_); - ESP_LOGCONFIG(TAG, " Max. Temperature: %.1f°C", this->maximum_temperature_); - ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_)); - ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_)); + ESP_LOGCONFIG(TAG, + " Min. Temperature: %.1f°C\n" + " Max. Temperature: %.1f°C\n" + " Supports HEAT: %s\n" + " Supports COOL: %s", + this->minimum_temperature_, this->maximum_temperature_, YESNO(this->supports_heat_), + YESNO(this->supports_cool_)); } } // namespace climate_ir diff --git a/esphome/components/cm1106/__init__.py b/esphome/components/cm1106/__init__.py new file mode 100644 index 0000000000..fa3c3f1925 --- /dev/null +++ b/esphome/components/cm1106/__init__.py @@ -0,0 +1 @@ +"""CM1106 component for ESPHome.""" diff --git a/esphome/components/cm1106/cm1106.cpp b/esphome/components/cm1106/cm1106.cpp new file mode 100644 index 0000000000..109524c04a --- /dev/null +++ b/esphome/components/cm1106/cm1106.cpp @@ -0,0 +1,112 @@ +#include "cm1106.h" +#include "esphome/core/log.h" + +#include + +namespace esphome { +namespace cm1106 { + +static const char *const TAG = "cm1106"; +static const uint8_t C_M1106_CMD_GET_CO2[4] = {0x11, 0x01, 0x01, 0xED}; +static const uint8_t C_M1106_CMD_SET_CO2_CALIB[6] = {0x11, 0x03, 0x03, 0x00, 0x00, 0x00}; +static const uint8_t C_M1106_CMD_SET_CO2_CALIB_RESPONSE[4] = {0x16, 0x01, 0x03, 0xE6}; + +uint8_t cm1106_checksum(const uint8_t *response, size_t len) { + uint8_t crc = 0; + for (int i = 0; i < len - 1; i++) { + crc -= response[i]; + } + return crc; +} + +void CM1106Component::setup() { + ESP_LOGCONFIG(TAG, "Running setup"); + uint8_t response[8] = {0}; + if (!this->cm1106_write_command_(C_M1106_CMD_GET_CO2, sizeof(C_M1106_CMD_GET_CO2), response, sizeof(response))) { + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); + this->mark_failed(); + return; + } +} + +void CM1106Component::update() { + uint8_t response[8] = {0}; + if (!this->cm1106_write_command_(C_M1106_CMD_GET_CO2, sizeof(C_M1106_CMD_GET_CO2), response, sizeof(response))) { + ESP_LOGW(TAG, "Reading data from CM1106 failed!"); + this->status_set_warning(); + return; + } + + if (response[0] != 0x16 || response[1] != 0x05 || response[2] != 0x01) { + ESP_LOGW(TAG, "Got wrong UART response from CM1106: %02X %02X %02X %02X", response[0], response[1], response[2], + response[3]); + this->status_set_warning(); + return; + } + + uint8_t checksum = cm1106_checksum(response, sizeof(response)); + if (response[7] != checksum) { + ESP_LOGW(TAG, "CM1106 Checksum doesn't match: 0x%02X!=0x%02X", response[7], checksum); + this->status_set_warning(); + return; + } + + this->status_clear_warning(); + + uint16_t ppm = response[3] << 8 | response[4]; + ESP_LOGD(TAG, "CM1106 Received CO₂=%uppm DF3=%02X DF4=%02X", ppm, response[5], response[6]); + if (this->co2_sensor_ != nullptr) + this->co2_sensor_->publish_state(ppm); +} + +void CM1106Component::calibrate_zero(uint16_t ppm) { + uint8_t cmd[6]; + memcpy(cmd, C_M1106_CMD_SET_CO2_CALIB, sizeof(cmd)); + cmd[3] = ppm >> 8; + cmd[4] = ppm & 0xFF; + uint8_t response[4] = {0}; + + if (!this->cm1106_write_command_(cmd, sizeof(cmd), response, sizeof(response))) { + ESP_LOGW(TAG, "Reading data from CM1106 failed!"); + this->status_set_warning(); + return; + } + + // check if correct response received + if (memcmp(response, C_M1106_CMD_SET_CO2_CALIB_RESPONSE, sizeof(response)) != 0) { + ESP_LOGW(TAG, "Got wrong UART response from CM1106: %02X %02X %02X %02X", response[0], response[1], response[2], + response[3]); + this->status_set_warning(); + return; + } + + this->status_clear_warning(); + ESP_LOGD(TAG, "CM1106 Successfully calibrated sensor to %uppm", ppm); +} + +bool CM1106Component::cm1106_write_command_(const uint8_t *command, size_t command_len, uint8_t *response, + size_t response_len) { + // Empty RX Buffer + while (this->available()) + this->read(); + this->write_array(command, command_len - 1); + this->write_byte(cm1106_checksum(command, command_len)); + this->flush(); + + if (response == nullptr) + return true; + + return this->read_array(response, response_len); +} + +void CM1106Component::dump_config() { + ESP_LOGCONFIG(TAG, "CM1106:"); + LOG_SENSOR(" ", "CO2", this->co2_sensor_); + this->check_uart_settings(9600); + if (this->is_failed()) { + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); + } +} + +} // namespace cm1106 +} // namespace esphome diff --git a/esphome/components/cm1106/cm1106.h b/esphome/components/cm1106/cm1106.h new file mode 100644 index 0000000000..3b78e17cf4 --- /dev/null +++ b/esphome/components/cm1106/cm1106.h @@ -0,0 +1,40 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/uart/uart.h" + +namespace esphome { +namespace cm1106 { + +class CM1106Component : public PollingComponent, public uart::UARTDevice { + public: + float get_setup_priority() const override { return esphome::setup_priority::DATA; } + + void setup() override; + void update() override; + void dump_config() override; + + void calibrate_zero(uint16_t ppm); + + void set_co2_sensor(sensor::Sensor *co2_sensor) { this->co2_sensor_ = co2_sensor; } + + protected: + sensor::Sensor *co2_sensor_{nullptr}; + + bool cm1106_write_command_(const uint8_t *command, size_t command_len, uint8_t *response, size_t response_len); +}; + +template class CM1106CalibrateZeroAction : public Action { + public: + CM1106CalibrateZeroAction(CM1106Component *cm1106) : cm1106_(cm1106) {} + + void play(Ts... x) override { this->cm1106_->calibrate_zero(400); } + + protected: + CM1106Component *cm1106_; +}; + +} // namespace cm1106 +} // namespace esphome diff --git a/esphome/components/cm1106/sensor.py b/esphome/components/cm1106/sensor.py new file mode 100644 index 0000000000..1b8ac14fbe --- /dev/null +++ b/esphome/components/cm1106/sensor.py @@ -0,0 +1,72 @@ +"""CM1106 Sensor component for ESPHome.""" + +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.automation import maybe_simple_id +from esphome.components import sensor, uart +from esphome.const import ( + CONF_CO2, + CONF_ID, + DEVICE_CLASS_CARBON_DIOXIDE, + ICON_MOLECULE_CO2, + STATE_CLASS_MEASUREMENT, + UNIT_PARTS_PER_MILLION, +) + +DEPENDENCIES = ["uart"] +CODEOWNERS = ["@andrewjswan"] + +cm1106_ns = cg.esphome_ns.namespace("cm1106") +CM1106Component = cm1106_ns.class_( + "CM1106Component", cg.PollingComponent, uart.UARTDevice +) +CM1106CalibrateZeroAction = cm1106_ns.class_( + "CM1106CalibrateZeroAction", + automation.Action, +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(CM1106Component), + cv.Optional(CONF_CO2): sensor.sensor_schema( + unit_of_measurement=UNIT_PARTS_PER_MILLION, + icon=ICON_MOLECULE_CO2, + accuracy_decimals=0, + device_class=DEVICE_CLASS_CARBON_DIOXIDE, + state_class=STATE_CLASS_MEASUREMENT, + ), + }, + ) + .extend(cv.polling_component_schema("60s")) + .extend(uart.UART_DEVICE_SCHEMA) +) + + +async def to_code(config) -> None: + """Code generation entry point.""" + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) + if co2_config := config.get(CONF_CO2): + sens = await sensor.new_sensor(co2_config) + cg.add(var.set_co2_sensor(sens)) + + +CALIBRATION_ACTION_SCHEMA = maybe_simple_id( + { + cv.GenerateID(): cv.use_id(CM1106Component), + }, +) + + +@automation.register_action( + "cm1106.calibrate_zero", + CM1106CalibrateZeroAction, + CALIBRATION_ACTION_SCHEMA, +) +async def cm1106_calibration_to_code(config, action_id, template_arg, args) -> None: + """Service code generation entry point.""" + paren = await cg.get_variable(config[CONF_ID]) + return cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/const/__init__.py b/esphome/components/const/__init__.py index 6af357f23b..a73849e67d 100644 --- a/esphome/components/const/__init__.py +++ b/esphome/components/const/__init__.py @@ -3,3 +3,4 @@ CODEOWNERS = ["@esphome/core"] CONF_DRAW_ROUNDING = "draw_rounding" +CONF_REQUEST_HEADERS = "request_headers" diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index 13f117c3f0..9fe7593eab 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -189,6 +189,7 @@ async def register_cover(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_cover(var)) + CORE.register_platform_component("cover", var) await setup_cover_core_(var, config) diff --git a/esphome/components/cs5460a/cs5460a.cpp b/esphome/components/cs5460a/cs5460a.cpp index 0d347ae0c1..e3a5941d94 100644 --- a/esphome/components/cs5460a/cs5460a.cpp +++ b/esphome/components/cs5460a/cs5460a.cpp @@ -52,7 +52,7 @@ bool CS5460AComponent::softreset_() { } void CS5460AComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up CS5460A..."); + ESP_LOGCONFIG(TAG, "Running setup"); float current_full_scale = (pga_gain_ == CS5460A_PGA_GAIN_10X) ? 0.25 : 0.10; float voltage_full_scale = 0.25; @@ -319,18 +319,23 @@ bool CS5460AComponent::check_status_() { void CS5460AComponent::dump_config() { uint32_t state = this->get_component_state(); - ESP_LOGCONFIG(TAG, "CS5460A:"); - ESP_LOGCONFIG(TAG, " Init status: %s", + ESP_LOGCONFIG(TAG, + "CS5460A:\n" + " Init status: %s", state == COMPONENT_STATE_LOOP ? "OK" : (state == COMPONENT_STATE_FAILED ? "failed" : "other")); LOG_PIN(" CS Pin: ", cs_); - ESP_LOGCONFIG(TAG, " Samples / cycle: %" PRIu32, samples_); - ESP_LOGCONFIG(TAG, " Phase offset: %i", phase_offset_); - ESP_LOGCONFIG(TAG, " PGA Gain: %s", pga_gain_ == CS5460A_PGA_GAIN_50X ? "50x" : "10x"); - ESP_LOGCONFIG(TAG, " Current gain: %.5f", current_gain_); - ESP_LOGCONFIG(TAG, " Voltage gain: %.5f", voltage_gain_); - ESP_LOGCONFIG(TAG, " Current HPF: %s", current_hpf_ ? "enabled" : "disabled"); - ESP_LOGCONFIG(TAG, " Voltage HPF: %s", voltage_hpf_ ? "enabled" : "disabled"); - ESP_LOGCONFIG(TAG, " Pulse energy: %.2f Wh", pulse_energy_wh_); + ESP_LOGCONFIG(TAG, + " Samples / cycle: %" PRIu32 "\n" + " Phase offset: %i\n" + " PGA Gain: %s\n" + " Current gain: %.5f\n" + " Voltage gain: %.5f\n" + " Current HPF: %s\n" + " Voltage HPF: %s\n" + " Pulse energy: %.2f Wh", + samples_, phase_offset_, pga_gain_ == CS5460A_PGA_GAIN_50X ? "50x" : "10x", current_gain_, + voltage_gain_, current_hpf_ ? "enabled" : "disabled", voltage_hpf_ ? "enabled" : "disabled", + pulse_energy_wh_); LOG_SENSOR(" ", "Voltage", voltage_sensor_); LOG_SENSOR(" ", "Current", current_sensor_); LOG_SENSOR(" ", "Power", power_sensor_); diff --git a/esphome/components/cse7761/cse7761.cpp b/esphome/components/cse7761/cse7761.cpp index 337996f6ef..6c3d457f26 100644 --- a/esphome/components/cse7761/cse7761.cpp +++ b/esphome/components/cse7761/cse7761.cpp @@ -42,7 +42,7 @@ static const uint8_t CSE7761_CMD_ENABLE_WRITE = 0xE5; // Enable write operation enum CSE7761 { RMS_IAC, RMS_IBC, RMS_UC, POWER_PAC, POWER_PBC, POWER_SC, ENERGY_AC, ENERGY_BC }; void CSE7761Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up CSE7761..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->write_(CSE7761_SPECIAL_COMMAND, CSE7761_CMD_RESET); uint16_t syscon = this->read_(0x00, 2); // Default 0x0A04 if ((0x0A04 == syscon) && this->chip_init_()) { @@ -57,7 +57,7 @@ void CSE7761Component::setup() { void CSE7761Component::dump_config() { ESP_LOGCONFIG(TAG, "CSE7761:"); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with CSE7761 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); this->check_uart_settings(38400, 1, uart::UART_CONFIG_PARITY_EVEN, 8); diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index b0876778a3..fe81ae91fe 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -223,11 +223,6 @@ void CSE7766Component::parse_data_() { #endif } -uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) { - return (uint32_t(this->raw_data_[start_index]) << 16) | (uint32_t(this->raw_data_[start_index + 1]) << 8) | - uint32_t(this->raw_data_[start_index + 2]); -} - void CSE7766Component::dump_config() { ESP_LOGCONFIG(TAG, "CSE7766:"); LOG_SENSOR(" ", "Voltage", this->voltage_sensor_); diff --git a/esphome/components/cse7766/cse7766.h b/esphome/components/cse7766/cse7766.h index 5d89b3b75b..8902eafe3c 100644 --- a/esphome/components/cse7766/cse7766.h +++ b/esphome/components/cse7766/cse7766.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/uart/uart.h" @@ -28,7 +29,10 @@ class CSE7766Component : public Component, public uart::UARTDevice { protected: bool check_byte_(); void parse_data_(); - uint32_t get_24_bit_uint_(uint8_t start_index); + uint32_t get_24_bit_uint_(uint8_t start_index) const { + return encode_uint24(this->raw_data_[start_index], this->raw_data_[start_index + 1], + this->raw_data_[start_index + 2]); + } uint8_t raw_data_[24]; uint8_t raw_data_index_{0}; diff --git a/esphome/components/cst226/touchscreen/cst226_touchscreen.cpp b/esphome/components/cst226/touchscreen/cst226_touchscreen.cpp index fa8cd9b057..c444dd7485 100644 --- a/esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +++ b/esphome/components/cst226/touchscreen/cst226_touchscreen.cpp @@ -6,7 +6,7 @@ namespace cst226 { static const char *const TAG = "cst226.touchscreen"; void CST226Touchscreen::setup() { - ESP_LOGCONFIG(TAG, "Setting up CST226 Touchscreen..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (this->reset_pin_ != nullptr) { this->reset_pin_->setup(); this->reset_pin_->digital_write(true); diff --git a/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp b/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp index 607f209c4a..0c5099d4f0 100644 --- a/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +++ b/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp @@ -1,4 +1,5 @@ #include "cst816_touchscreen.h" +#include "esphome/core/helpers.h" namespace esphome { namespace cst816 { @@ -38,7 +39,7 @@ void CST816Touchscreen::continue_setup_() { } void CST816Touchscreen::setup() { - ESP_LOGCONFIG(TAG, "Setting up CST816 Touchscreen..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (this->reset_pin_ != nullptr) { this->reset_pin_->setup(); this->reset_pin_->digital_write(true); @@ -74,8 +75,10 @@ void CST816Touchscreen::dump_config() { LOG_I2C_DEVICE(this); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " X Raw Min: %d, X Raw Max: %d", this->x_raw_min_, this->x_raw_max_); - ESP_LOGCONFIG(TAG, " Y Raw Min: %d, Y Raw Max: %d", this->y_raw_min_, this->y_raw_max_); + ESP_LOGCONFIG(TAG, + " X Raw Min: %d, X Raw Max: %d\n" + " Y Raw Min: %d, Y Raw Max: %d", + this->x_raw_min_, this->x_raw_max_, this->y_raw_min_, this->y_raw_max_); const char *name; switch (this->chip_id_) { case CST820_CHIP_ID: diff --git a/esphome/components/current_based/current_based_cover.cpp b/esphome/components/current_based/current_based_cover.cpp index 8bb27dbeca..895b5515cb 100644 --- a/esphome/components/current_based/current_based_cover.cpp +++ b/esphome/components/current_based/current_based_cover.cpp @@ -151,8 +151,10 @@ void CurrentBasedCover::dump_config() { if (this->max_duration_ != UINT32_MAX) { ESP_LOGCONFIG(TAG, "Maximum duration: %.1fs", this->max_duration_ / 1e3f); } - ESP_LOGCONFIG(TAG, "Start sensing delay: %.1fs", this->start_sensing_delay_ / 1e3f); - ESP_LOGCONFIG(TAG, "Malfunction detection: %s", YESNO(this->malfunction_detection_)); + ESP_LOGCONFIG(TAG, + "Start sensing delay: %.1fs\n" + "Malfunction detection: %s", + this->start_sensing_delay_ / 1e3f, YESNO(this->malfunction_detection_)); } float CurrentBasedCover::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/dac7678/dac7678_output.cpp b/esphome/components/dac7678/dac7678_output.cpp index b6de615b30..5c10bbc1bc 100644 --- a/esphome/components/dac7678/dac7678_output.cpp +++ b/esphome/components/dac7678/dac7678_output.cpp @@ -1,7 +1,7 @@ #include "dac7678_output.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace dac7678 { @@ -20,9 +20,9 @@ static const uint8_t DAC7678_REG_INTERNAL_REF_0 = 0x80; static const uint8_t DAC7678_REG_INTERNAL_REF_1 = 0x90; void DAC7678Output::setup() { - ESP_LOGCONFIG(TAG, "Setting up DAC7678OutputComponent..."); + ESP_LOGCONFIG(TAG, "Running setup"); - ESP_LOGV(TAG, "Resetting device..."); + ESP_LOGV(TAG, "Resetting device"); // Reset device if (!this->write_byte_16(DAC7678_REG_SOFTWARE_RESET, 0x0000)) { diff --git a/esphome/components/dallas_temp/dallas_temp.cpp b/esphome/components/dallas_temp/dallas_temp.cpp index 46db22d97f..3796a888fd 100644 --- a/esphome/components/dallas_temp/dallas_temp.cpp +++ b/esphome/components/dallas_temp/dallas_temp.cpp @@ -70,7 +70,7 @@ bool DallasTemperatureSensor::read_scratch_pad_() { } void DallasTemperatureSensor::setup() { - ESP_LOGCONFIG(TAG, "setting up Dallas temperature sensor..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->check_address_()) return; if (!this->read_scratch_pad_()) @@ -80,7 +80,7 @@ void DallasTemperatureSensor::setup() { if ((this->address_ & 0xff) == DALLAS_MODEL_DS18S20) { // DS18S20 doesn't support resolution. - ESP_LOGW(TAG, "DS18S20 doesn't support setting resolution."); + ESP_LOGW(TAG, "DS18S20 doesn't support setting resolution"); return; } @@ -125,7 +125,6 @@ bool DallasTemperatureSensor::check_scratch_pad_() { crc8(this->scratch_pad_, 8)); #endif if (!chksum_validity) { - ESP_LOGW(TAG, "'%s' - Scratch pad checksum invalid!", this->get_name().c_str()); this->status_set_warning("scratch pad checksum invalid"); ESP_LOGD(TAG, "Scratch pad: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X (%02X)", this->scratch_pad_[0], this->scratch_pad_[1], this->scratch_pad_[2], this->scratch_pad_[3], this->scratch_pad_[4], diff --git a/esphome/components/daly_bms/daly_bms.cpp b/esphome/components/daly_bms/daly_bms.cpp index 1dd0520465..2d270cc56e 100644 --- a/esphome/components/daly_bms/daly_bms.cpp +++ b/esphome/components/daly_bms/daly_bms.cpp @@ -1,7 +1,8 @@ #include "daly_bms.h" #include -#include "esphome/core/log.h" #include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace daly_bms { diff --git a/esphome/components/dashboard_import/__init__.py b/esphome/components/dashboard_import/__init__.py index acaadab544..dbe5532902 100644 --- a/esphome/components/dashboard_import/__init__.py +++ b/esphome/components/dashboard_import/__init__.py @@ -2,7 +2,6 @@ import base64 from pathlib import Path import re import secrets -from typing import Optional import requests from ruamel.yaml import YAML @@ -84,7 +83,7 @@ async def to_code(config): def import_config( path: str, name: str, - friendly_name: Optional[str], + friendly_name: str | None, project_name: str, import_url: str, network: str = CONF_WIFI, diff --git a/esphome/components/datetime/__init__.py b/esphome/components/datetime/__init__.py index 630bf6962c..24fbf5a1ec 100644 --- a/esphome/components/datetime/__init__.py +++ b/esphome/components/datetime/__init__.py @@ -158,7 +158,9 @@ async def setup_datetime_core_(var, config): async def register_datetime(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) - cg.add(getattr(cg.App, f"register_{config[CONF_TYPE].lower()}")(var)) + entity_type = config[CONF_TYPE].lower() + cg.add(getattr(cg.App, f"register_{entity_type}")(var)) + CORE.register_platform_component(entity_type, var) await setup_datetime_core_(var, config) cg.add_define(f"USE_DATETIME_{config[CONF_TYPE]}") diff --git a/esphome/components/datetime/date_entity.cpp b/esphome/components/datetime/date_entity.cpp index b5bcef43af..c164a98b2e 100644 --- a/esphome/components/datetime/date_entity.cpp +++ b/esphome/components/datetime/date_entity.cpp @@ -11,25 +11,25 @@ static const char *const TAG = "datetime.date_entity"; void DateEntity::publish_state() { if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) { - this->has_state_ = false; + this->set_has_state(false); return; } if (this->year_ < 1970 || this->year_ > 3000) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Year must be between 1970 and 3000"); return; } if (this->month_ < 1 || this->month_ > 12) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Month must be between 1 and 12"); return; } if (this->day_ > days_in_month(this->month_, this->year_)) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_); return; } - this->has_state_ = true; + this->set_has_state(true); ESP_LOGD(TAG, "'%s': Sending date %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_); this->state_callback_.call(); } diff --git a/esphome/components/datetime/datetime_base.h b/esphome/components/datetime/datetime_base.h index dea34e6110..b7645f5539 100644 --- a/esphome/components/datetime/datetime_base.h +++ b/esphome/components/datetime/datetime_base.h @@ -13,9 +13,6 @@ namespace datetime { class DateTimeBase : public EntityBase { public: - /// Return whether this Datetime has gotten a full state yet. - bool has_state() const { return this->has_state_; } - virtual ESPTime state_as_esptime() const = 0; void add_on_state_callback(std::function &&callback) { this->state_callback_.add(std::move(callback)); } @@ -31,8 +28,6 @@ class DateTimeBase : public EntityBase { #ifdef USE_TIME time::RealTimeClock *rtc_; #endif - - bool has_state_{false}; }; #ifdef USE_TIME diff --git a/esphome/components/datetime/datetime_entity.cpp b/esphome/components/datetime/datetime_entity.cpp index 3d92194efa..4e3b051eb3 100644 --- a/esphome/components/datetime/datetime_entity.cpp +++ b/esphome/components/datetime/datetime_entity.cpp @@ -11,40 +11,40 @@ static const char *const TAG = "datetime.datetime_entity"; void DateTimeEntity::publish_state() { if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) { - this->has_state_ = false; + this->set_has_state(false); return; } if (this->year_ < 1970 || this->year_ > 3000) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Year must be between 1970 and 3000"); return; } if (this->month_ < 1 || this->month_ > 12) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Month must be between 1 and 12"); return; } if (this->day_ > days_in_month(this->month_, this->year_)) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_); return; } if (this->hour_ > 23) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Hour must be between 0 and 23"); return; } if (this->minute_ > 59) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Minute must be between 0 and 59"); return; } if (this->second_ > 59) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Second must be between 0 and 59"); return; } - this->has_state_ = true; + this->set_has_state(true); ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_, this->month_, this->day_, this->hour_, this->minute_, this->second_); this->state_callback_.call(); diff --git a/esphome/components/datetime/time_entity.cpp b/esphome/components/datetime/time_entity.cpp index db0094ae01..9b05c2124f 100644 --- a/esphome/components/datetime/time_entity.cpp +++ b/esphome/components/datetime/time_entity.cpp @@ -11,21 +11,21 @@ static const char *const TAG = "datetime.time_entity"; void TimeEntity::publish_state() { if (this->hour_ > 23) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Hour must be between 0 and 23"); return; } if (this->minute_ > 59) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Minute must be between 0 and 59"); return; } if (this->second_ > 59) { - this->has_state_ = false; + this->set_has_state(false); ESP_LOGE(TAG, "Second must be between 0 and 59"); return; } - this->has_state_ = true; + this->set_has_state(true); ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_, this->second_); this->state_callback_.call(); diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index 41e07dac35..ade0968e08 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -2,9 +2,9 @@ #include #include "esphome/core/application.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/core/version.h" #include #include diff --git a/esphome/components/debug/debug_component.h b/esphome/components/debug/debug_component.h index 50d26ad2ea..efd0dafab0 100644 --- a/esphome/components/debug/debug_component.h +++ b/esphome/components/debug/debug_component.h @@ -2,8 +2,8 @@ #include "esphome/core/component.h" #include "esphome/core/defines.h" -#include "esphome/core/macros.h" #include "esphome/core/helpers.h" +#include "esphome/core/macros.h" #ifdef USE_SENSOR #include "esphome/components/sensor/sensor.h" diff --git a/esphome/components/debug/debug_esp32.cpp b/esphome/components/debug/debug_esp32.cpp index 999cb927b3..e48a4941b3 100644 --- a/esphome/components/debug/debug_esp32.cpp +++ b/esphome/components/debug/debug_esp32.cpp @@ -107,8 +107,10 @@ std::string DebugComponent::get_wakeup_cause_() { } void DebugComponent::log_partition_info_() { - ESP_LOGCONFIG(TAG, "Partition table:"); - ESP_LOGCONFIG(TAG, " %-12s %-4s %-8s %-10s %-10s", "Name", "Type", "Subtype", "Address", "Size"); + ESP_LOGCONFIG(TAG, + "Partition table:\n" + " %-12s %-4s %-8s %-10s %-10s", + "Name", "Type", "Subtype", "Address", "Size"); esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL); while (it != NULL) { const esp_partition_t *partition = esp_partition_get(it); diff --git a/esphome/components/deep_sleep/deep_sleep_component.cpp b/esphome/components/deep_sleep/deep_sleep_component.cpp index 1e7637f3e5..84fc102b66 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.cpp +++ b/esphome/components/deep_sleep/deep_sleep_component.cpp @@ -6,24 +6,26 @@ namespace esphome { namespace deep_sleep { static const char *const TAG = "deep_sleep"; +// 5 seconds for deep sleep to ensure clean disconnect from Home Assistant +static const uint32_t TEARDOWN_TIMEOUT_DEEP_SLEEP_MS = 5000; bool global_has_deep_sleep = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void DeepSleepComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up Deep Sleep..."); + ESP_LOGCONFIG(TAG, "Running setup"); global_has_deep_sleep = true; const optional run_duration = get_run_duration_(); if (run_duration.has_value()) { - ESP_LOGI(TAG, "Scheduling Deep Sleep to start in %" PRIu32 " ms", *run_duration); + ESP_LOGI(TAG, "Scheduling in %" PRIu32 " ms", *run_duration); this->set_timeout(*run_duration, [this]() { this->begin_sleep(); }); } else { - ESP_LOGD(TAG, "Not scheduling Deep Sleep, as no run duration is configured."); + ESP_LOGD(TAG, "Not scheduling; no run duration configured"); } } void DeepSleepComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Setting up Deep Sleep..."); + ESP_LOGCONFIG(TAG, "Deep sleep:"); if (this->sleep_duration_.has_value()) { uint32_t duration = *this->sleep_duration_ / 1000; ESP_LOGCONFIG(TAG, " Sleep Duration: %" PRIu32 " ms", duration); @@ -57,11 +59,15 @@ void DeepSleepComponent::begin_sleep(bool manual) { return; } - ESP_LOGI(TAG, "Beginning Deep Sleep"); + ESP_LOGI(TAG, "Beginning sleep"); if (this->sleep_duration_.has_value()) { ESP_LOGI(TAG, "Sleeping for %" PRId64 "us", *this->sleep_duration_); } App.run_safe_shutdown_hooks(); + // It's critical to teardown components cleanly for deep sleep to ensure + // Home Assistant sees a clean disconnect instead of marking the device unavailable + App.teardown_components(TEARDOWN_TIMEOUT_DEEP_SLEEP_MS); + App.run_powerdown_hooks(); this->deep_sleep_(); } diff --git a/esphome/components/deep_sleep/deep_sleep_esp32.cpp b/esphome/components/deep_sleep/deep_sleep_esp32.cpp index 4582d695f6..7965ab738a 100644 --- a/esphome/components/deep_sleep/deep_sleep_esp32.cpp +++ b/esphome/components/deep_sleep/deep_sleep_esp32.cpp @@ -46,10 +46,12 @@ void DeepSleepComponent::dump_config_platform_() { LOG_PIN(" Wakeup Pin: ", this->wakeup_pin_); } if (this->wakeup_cause_to_run_duration_.has_value()) { - ESP_LOGCONFIG(TAG, " Default Wakeup Run Duration: %" PRIu32 " ms", - this->wakeup_cause_to_run_duration_->default_cause); - ESP_LOGCONFIG(TAG, " Touch Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->touch_cause); - ESP_LOGCONFIG(TAG, " GPIO Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->gpio_cause); + ESP_LOGCONFIG(TAG, + " Default Wakeup Run Duration: %" PRIu32 " ms\n" + " Touch Wakeup Run Duration: %" PRIu32 " ms\n" + " GPIO Wakeup Run Duration: %" PRIu32 " ms", + this->wakeup_cause_to_run_duration_->default_cause, this->wakeup_cause_to_run_duration_->touch_cause, + this->wakeup_cause_to_run_duration_->gpio_cause); } } @@ -59,7 +61,7 @@ bool DeepSleepComponent::prepare_to_sleep_() { // Defer deep sleep until inactive if (!this->next_enter_deep_sleep_) { this->status_set_warning(); - ESP_LOGW(TAG, "Waiting wakeup pin state change to enter deep sleep..."); + ESP_LOGW(TAG, "Waiting for wakeup pin state change"); } this->next_enter_deep_sleep_ = true; return false; diff --git a/esphome/components/demo/__init__.py b/esphome/components/demo/__init__.py index 96ffb58b82..0a56073284 100644 --- a/esphome/components/demo/__init__.py +++ b/esphome/components/demo/__init__.py @@ -1,14 +1,22 @@ import esphome.codegen as cg from esphome.components import ( + alarm_control_panel, binary_sensor, + button, climate, cover, + datetime, + event, fan, light, + lock, number, + select, sensor, switch, + text, text_sensor, + valve, ) import esphome.config_validation as cv from esphome.const import ( @@ -20,7 +28,9 @@ from esphome.const import ( CONF_INVERTED, CONF_MAX_VALUE, CONF_MIN_VALUE, + CONF_MODE, CONF_NAME, + CONF_OPTIONS, CONF_OUTPUT_ID, CONF_SENSORS, CONF_STATE_CLASS, @@ -31,9 +41,11 @@ from esphome.const import ( CONF_UNIT_OF_MEASUREMENT, DEVICE_CLASS_ENERGY, DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_IDENTIFY, DEVICE_CLASS_MOISTURE, DEVICE_CLASS_MOTION, DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_UPDATE, ICON_BLUETOOTH, ICON_BLUR, ICON_THERMOMETER, @@ -45,38 +57,68 @@ from esphome.const import ( ) AUTO_LOAD = [ + "alarm_control_panel", "binary_sensor", + "button", "climate", "cover", + "datetime", + "event", "fan", "light", + "lock", "number", + "select", "sensor", "switch", + "text", "text_sensor", + "valve", ] demo_ns = cg.esphome_ns.namespace("demo") +DemoAlarmControlPanel = demo_ns.class_( + "DemoAlarmControlPanel", alarm_control_panel.AlarmControlPanel, cg.Component +) +DemoAlarmControlPanelType = demo_ns.enum("DemoAlarmControlPanelType", is_class=True) DemoBinarySensor = demo_ns.class_( "DemoBinarySensor", binary_sensor.BinarySensor, cg.PollingComponent ) +DemoButton = demo_ns.class_("DemoButton", button.Button) DemoClimate = demo_ns.class_("DemoClimate", climate.Climate, cg.Component) DemoClimateType = demo_ns.enum("DemoClimateType", is_class=True) DemoCover = demo_ns.class_("DemoCover", cover.Cover, cg.Component) DemoCoverType = demo_ns.enum("DemoCoverType", is_class=True) +DemoDate = demo_ns.class_("DemoDate", datetime.DateEntity, cg.Component) +DemoDateTime = demo_ns.class_("DemoDateTime", datetime.DateTimeEntity, cg.Component) +DemoTime = demo_ns.class_("DemoTime", datetime.TimeEntity, cg.Component) +DemoEvent = demo_ns.class_("DemoEvent", event.Event, cg.Component) DemoFan = demo_ns.class_("DemoFan", fan.Fan, cg.Component) DemoFanType = demo_ns.enum("DemoFanType", is_class=True) DemoLight = demo_ns.class_("DemoLight", light.LightOutput, cg.Component) DemoLightType = demo_ns.enum("DemoLightType", is_class=True) +DemoLock = demo_ns.class_("DemoLock", lock.Lock, cg.Component) +DemoLockType = demo_ns.enum("DemoLockType", is_class=True) DemoNumber = demo_ns.class_("DemoNumber", number.Number, cg.Component) DemoNumberType = demo_ns.enum("DemoNumberType", is_class=True) +DemoSelect = demo_ns.class_("DemoSelect", select.Select, cg.Component) +DemoSelectType = demo_ns.enum("DemoSelectType", is_class=True) DemoSensor = demo_ns.class_("DemoSensor", sensor.Sensor, cg.PollingComponent) DemoSwitch = demo_ns.class_("DemoSwitch", switch.Switch, cg.Component) +DemoText = demo_ns.class_("DemoText", text.Text, cg.Component) +DemoTextType = demo_ns.enum("DemoTextType", is_class=True) DemoTextSensor = demo_ns.class_( "DemoTextSensor", text_sensor.TextSensor, cg.PollingComponent ) +DemoValve = demo_ns.class_("DemoValve", valve.Valve) +DemoValveType = demo_ns.enum("DemoValveType", is_class=True) +ALARM_CONTROL_PANEL_TYPES = { + 1: DemoAlarmControlPanelType.TYPE_1, + 2: DemoAlarmControlPanelType.TYPE_2, + 3: DemoAlarmControlPanelType.TYPE_3, +} CLIMATE_TYPES = { 1: DemoClimateType.TYPE_1, 2: DemoClimateType.TYPE_2, @@ -103,21 +145,67 @@ LIGHT_TYPES = { 6: DemoLightType.TYPE_6, 7: DemoLightType.TYPE_7, } +LOCK_TYPES = { + 1: DemoLockType.TYPE_1, + 2: DemoLockType.TYPE_2, +} NUMBER_TYPES = { 1: DemoNumberType.TYPE_1, 2: DemoNumberType.TYPE_2, 3: DemoNumberType.TYPE_3, } +TEXT_TYPES = { + 1: DemoTextType.TYPE_1, + 2: DemoTextType.TYPE_2, +} +VALVE_TYPES = { + 1: DemoValveType.TYPE_1, + 2: DemoValveType.TYPE_2, +} +CONF_ALARM_CONTROL_PANELS = "alarm_control_panels" +CONF_BUTTONS = "buttons" CONF_CLIMATES = "climates" CONF_COVERS = "covers" +CONF_DATETIMES = "datetimes" CONF_FANS = "fans" CONF_LIGHTS = "lights" +CONF_LOCKS = "locks" CONF_NUMBERS = "numbers" +CONF_SELECTS = "selects" +CONF_TEXTS = "texts" +CONF_VALVES = "valves" CONFIG_SCHEMA = cv.Schema( { + cv.Optional( + CONF_ALARM_CONTROL_PANELS, + default=[ + { + CONF_NAME: "Demo Alarm Control Panel", + CONF_TYPE: 1, + }, + { + CONF_NAME: "Demo Alarm Control Panel Code", + CONF_TYPE: 2, + }, + { + CONF_NAME: "Demo Alarm Control Panel Code to Arm", + CONF_TYPE: 3, + }, + ], + ): [ + alarm_control_panel.alarm_control_panel_schema( + DemoAlarmControlPanel + ).extend( + { + cv.Required(CONF_TYPE): cv.enum( + ALARM_CONTROL_PANEL_TYPES, int=True + ), + } + ) + ], cv.Optional( CONF_BINARY_SENSORS, default=[ @@ -135,6 +223,21 @@ CONFIG_SCHEMA = cv.Schema( cv.polling_component_schema("60s") ) ], + cv.Optional( + CONF_BUTTONS, + default=[ + { + CONF_NAME: "Demo Update Button", + CONF_DEVICE_CLASS: DEVICE_CLASS_UPDATE, + }, + { + CONF_NAME: "Demo Button Identify", + CONF_DEVICE_CLASS: DEVICE_CLASS_IDENTIFY, + }, + ], + ): [ + button.button_schema(DemoButton), + ], cv.Optional( CONF_CLIMATES, default=[ @@ -191,6 +294,20 @@ CONFIG_SCHEMA = cv.Schema( } ) ], + cv.Optional( + CONF_DATETIMES, + default=[ + {CONF_NAME: "Demo DateTime", CONF_TYPE: "DATETIME"}, + {CONF_NAME: "Demo Date", CONF_TYPE: "DATE"}, + {CONF_NAME: "Demo Time", CONF_TYPE: "TIME"}, + ], + ): [ + cv.Any( + datetime.date_schema(DemoDate), + datetime.datetime_schema(DemoDateTime), + datetime.time_schema(DemoTime), + ) + ], cv.Optional( CONF_FANS, default=[ @@ -262,6 +379,19 @@ CONFIG_SCHEMA = cv.Schema( } ) ], + cv.Optional( + CONF_LOCKS, + default=[ + {CONF_NAME: "Demo Lock", CONF_TYPE: 1}, + {CONF_NAME: "Demo Lock and Open", CONF_TYPE: 2}, + ], + ): [ + lock.lock_schema(DemoLock).extend( + { + cv.Required(CONF_TYPE): cv.enum(LOCK_TYPES, int=True), + } + ) + ], cv.Optional( CONF_NUMBERS, default=[ @@ -299,6 +429,25 @@ CONFIG_SCHEMA = cv.Schema( } ) ], + cv.Optional( + CONF_SELECTS, + default=[ + { + CONF_NAME: "Demo Select 1", + CONF_OPTIONS: ["Option 1", "Option 2", "Option 3"], + }, + { + CONF_NAME: "Demo Select 2", + CONF_OPTIONS: ["Option A", "Option B", "Option C"], + }, + ], + ): [ + select.select_schema(DemoSelect).extend( + { + cv.Required(CONF_OPTIONS): cv.ensure_list(cv.string_strict), + } + ) + ], cv.Optional( CONF_SENSORS, default=[ @@ -355,6 +504,19 @@ CONFIG_SCHEMA = cv.Schema( }, ], ): [switch.switch_schema(DemoSwitch).extend(cv.COMPONENT_SCHEMA)], + cv.Optional( + CONF_TEXTS, + default=[ + {CONF_NAME: "Demo Text 1", CONF_MODE: "TEXT", CONF_TYPE: 1}, + {CONF_NAME: "Demo Text 2", CONF_MODE: "PASSWORD", CONF_TYPE: 2}, + ], + ): [ + text.text_schema(DemoText).extend( + { + cv.Required(CONF_TYPE): cv.enum(TEXT_TYPES, int=True), + } + ) + ], cv.Optional( CONF_TEXT_SENSORS, default=[ @@ -371,15 +533,36 @@ CONFIG_SCHEMA = cv.Schema( cv.polling_component_schema("60s") ) ], + cv.Optional( + CONF_VALVES, + default=[ + {CONF_NAME: "Demo Valve 1", CONF_TYPE: 1}, + {CONF_NAME: "Demo Valve 2", CONF_TYPE: 2}, + ], + ): [ + valve.valve_schema(DemoValve).extend( + { + cv.Required(CONF_TYPE): cv.enum(VALVE_TYPES, int=True), + } + ) + ], } ) async def to_code(config): + for conf in config[CONF_ALARM_CONTROL_PANELS]: + var = await alarm_control_panel.new_alarm_control_panel(conf) + cg.add(var.set_type(conf[CONF_TYPE])) + await cg.register_component(var, conf) + for conf in config[CONF_BINARY_SENSORS]: var = await binary_sensor.new_binary_sensor(conf) await cg.register_component(var, conf) + for conf in config[CONF_BUTTONS]: + await button.new_button(conf) + for conf in config[CONF_CLIMATES]: var = await climate.new_climate(conf) await cg.register_component(var, conf) @@ -390,6 +573,10 @@ async def to_code(config): await cg.register_component(var, conf) cg.add(var.set_type(conf[CONF_TYPE])) + for conf in config[CONF_DATETIMES]: + var = await datetime.new_datetime(conf) + await cg.register_component(var, conf) + for conf in config[CONF_FANS]: var = await fan.new_fan(conf) await cg.register_component(var, conf) @@ -400,6 +587,11 @@ async def to_code(config): await cg.register_component(var, conf) cg.add(var.set_type(conf[CONF_TYPE])) + for conf in config[CONF_LOCKS]: + var = await lock.new_lock(conf) + if conf[CONF_TYPE] == 2: + cg.add(var.traits.set_supports_open(True)) + for conf in config[CONF_NUMBERS]: var = await number.new_number( conf, @@ -410,6 +602,10 @@ async def to_code(config): await cg.register_component(var, conf) cg.add(var.set_type(conf[CONF_TYPE])) + for conf in config[CONF_SELECTS]: + var = await select.new_select(conf, options=conf[CONF_OPTIONS]) + await cg.register_component(var, conf) + for conf in config[CONF_SENSORS]: var = await sensor.new_sensor(conf) await cg.register_component(var, conf) @@ -418,6 +614,16 @@ async def to_code(config): var = await switch.new_switch(conf) await cg.register_component(var, conf) + for conf in config[CONF_TEXTS]: + var = await text.new_text(conf) + await cg.register_component(var, conf) + if conf[CONF_TYPE] == 2: + cg.add(var.traits.set_mode(text.TextMode.TEXT_MODE_PASSWORD)) + for conf in config[CONF_TEXT_SENSORS]: var = await text_sensor.new_text_sensor(conf) await cg.register_component(var, conf) + + for conf in config[CONF_VALVES]: + var = await valve.new_valve(conf) + cg.add(var.set_type(conf[CONF_TYPE])) diff --git a/esphome/components/demo/demo_alarm_control_panel.h b/esphome/components/demo/demo_alarm_control_panel.h new file mode 100644 index 0000000000..9902d27882 --- /dev/null +++ b/esphome/components/demo/demo_alarm_control_panel.h @@ -0,0 +1,65 @@ +#pragma once + +#include "esphome/components/alarm_control_panel/alarm_control_panel.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace demo { + +using namespace alarm_control_panel; + +enum class DemoAlarmControlPanelType { + TYPE_1, + TYPE_2, + TYPE_3, +}; + +class DemoAlarmControlPanel : public AlarmControlPanel, public Component { + public: + void setup() override {} + + uint32_t get_supported_features() const override { return ACP_FEAT_ARM_AWAY | ACP_FEAT_TRIGGER; } + + bool get_requires_code() const override { return this->type_ != DemoAlarmControlPanelType::TYPE_1; } + + bool get_requires_code_to_arm() const override { return this->type_ == DemoAlarmControlPanelType::TYPE_3; } + + void set_type(DemoAlarmControlPanelType type) { this->type_ = type; } + + protected: + void control(const AlarmControlPanelCall &call) override { + auto state = call.get_state().value_or(ACP_STATE_DISARMED); + switch (state) { + case ACP_STATE_ARMED_AWAY: + if (this->get_requires_code_to_arm() && call.get_code().has_value()) { + if (call.get_code().value() != "1234") { + this->status_momentary_error("Invalid code", 5000); + return; + } + } + this->publish_state(ACP_STATE_ARMED_AWAY); + break; + case ACP_STATE_DISARMED: + if (this->get_requires_code() && call.get_code().has_value()) { + if (call.get_code().value() != "1234") { + this->status_momentary_error("Invalid code", 5000); + return; + } + } + this->publish_state(ACP_STATE_DISARMED); + return; + case ACP_STATE_TRIGGERED: + this->publish_state(ACP_STATE_TRIGGERED); + return; + case ACP_STATE_PENDING: + this->publish_state(ACP_STATE_PENDING); + return; + default: + break; + } + } + DemoAlarmControlPanelType type_{}; +}; + +} // namespace demo +} // namespace esphome diff --git a/esphome/components/demo/demo_button.h b/esphome/components/demo/demo_button.h new file mode 100644 index 0000000000..be80d26a8a --- /dev/null +++ b/esphome/components/demo/demo_button.h @@ -0,0 +1,15 @@ +#pragma once + +#include "esphome/components/button/button.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace demo { + +class DemoButton : public button::Button { + protected: + void press_action() override {} +}; + +} // namespace demo +} // namespace esphome diff --git a/esphome/components/demo/demo_date.h b/esphome/components/demo/demo_date.h new file mode 100644 index 0000000000..e09ab5f887 --- /dev/null +++ b/esphome/components/demo/demo_date.h @@ -0,0 +1,34 @@ +#pragma once + +#include "esphome/core/defines.h" + +#ifdef USE_DATETIME_DATE + +#include "esphome/components/datetime/date_entity.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace demo { + +class DemoDate : public datetime::DateEntity, public Component { + public: + void setup() override { + this->year_ = 2038; + this->month_ = 01; + this->day_ = 19; + this->publish_state(); + } + + protected: + void control(const datetime::DateCall &call) override { + this->year_ = call.get_year().value_or(this->year_); + this->month_ = call.get_month().value_or(this->month_); + this->day_ = call.get_day().value_or(this->day_); + this->publish_state(); + } +}; + +} // namespace demo +} // namespace esphome + +#endif diff --git a/esphome/components/demo/demo_datetime.h b/esphome/components/demo/demo_datetime.h new file mode 100644 index 0000000000..5ebcc3e64e --- /dev/null +++ b/esphome/components/demo/demo_datetime.h @@ -0,0 +1,40 @@ +#pragma once + +#include "esphome/core/defines.h" + +#ifdef USE_DATETIME_DATETIME + +#include "esphome/components/datetime/datetime_entity.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace demo { + +class DemoDateTime : public datetime::DateTimeEntity, public Component { + public: + void setup() override { + this->year_ = 2038; + this->month_ = 01; + this->day_ = 19; + this->hour_ = 3; + this->minute_ = 14; + this->second_ = 8; + this->publish_state(); + } + + protected: + void control(const datetime::DateTimeCall &call) override { + this->year_ = call.get_year().value_or(this->year_); + this->month_ = call.get_month().value_or(this->month_); + this->day_ = call.get_day().value_or(this->day_); + this->hour_ = call.get_hour().value_or(this->hour_); + this->minute_ = call.get_minute().value_or(this->minute_); + this->second_ = call.get_second().value_or(this->second_); + this->publish_state(); + } +}; + +} // namespace demo +} // namespace esphome + +#endif diff --git a/esphome/components/demo/demo_lock.h b/esphome/components/demo/demo_lock.h new file mode 100644 index 0000000000..94d0f70a14 --- /dev/null +++ b/esphome/components/demo/demo_lock.h @@ -0,0 +1,17 @@ +#pragma once + +#include "esphome/components/lock/lock.h" + +namespace esphome { +namespace demo { + +class DemoLock : public lock::Lock { + protected: + void control(const lock::LockCall &call) override { + auto state = *call.get_state(); + this->publish_state(state); + } +}; + +} // namespace demo +} // namespace esphome diff --git a/esphome/components/demo/demo_select.h b/esphome/components/demo/demo_select.h new file mode 100644 index 0000000000..1951a684a2 --- /dev/null +++ b/esphome/components/demo/demo_select.h @@ -0,0 +1,15 @@ +#pragma once + +#include "esphome/components/select/select.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace demo { + +class DemoSelect : public select::Select, public Component { + protected: + void control(const std::string &value) override { this->publish_state(value); } +}; + +} // namespace demo +} // namespace esphome diff --git a/esphome/components/demo/demo_text.h b/esphome/components/demo/demo_text.h new file mode 100644 index 0000000000..a753175062 --- /dev/null +++ b/esphome/components/demo/demo_text.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/text/text.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace demo { + +class DemoText : public text::Text, public Component { + public: + void setup() override { this->publish_state("I am a text entity"); } + + protected: + void control(const std::string &value) override { this->publish_state(value); } +}; + +} // namespace demo +} // namespace esphome diff --git a/esphome/components/demo/demo_time.h b/esphome/components/demo/demo_time.h new file mode 100644 index 0000000000..42788504bb --- /dev/null +++ b/esphome/components/demo/demo_time.h @@ -0,0 +1,34 @@ +#pragma once + +#include "esphome/core/defines.h" + +#ifdef USE_DATETIME_TIME + +#include "esphome/components/datetime/time_entity.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace demo { + +class DemoTime : public datetime::TimeEntity, public Component { + public: + void setup() override { + this->hour_ = 3; + this->minute_ = 14; + this->second_ = 8; + this->publish_state(); + } + + protected: + void control(const datetime::TimeCall &call) override { + this->hour_ = call.get_hour().value_or(this->hour_); + this->minute_ = call.get_minute().value_or(this->minute_); + this->second_ = call.get_second().value_or(this->second_); + this->publish_state(); + } +}; + +} // namespace demo +} // namespace esphome + +#endif diff --git a/esphome/components/demo/demo_valve.h b/esphome/components/demo/demo_valve.h new file mode 100644 index 0000000000..55d457f176 --- /dev/null +++ b/esphome/components/demo/demo_valve.h @@ -0,0 +1,54 @@ +#pragma once + +#include "esphome/components/valve/valve.h" + +namespace esphome { +namespace demo { + +enum class DemoValveType { + TYPE_1, + TYPE_2, +}; + +class DemoValve : public valve::Valve { + public: + valve::ValveTraits get_traits() override { + valve::ValveTraits traits; + if (this->type_ == DemoValveType::TYPE_2) { + traits.set_supports_position(true); + traits.set_supports_toggle(true); + traits.set_supports_stop(true); + } + return traits; + } + + void set_type(DemoValveType type) { this->type_ = type; } + + protected: + void control(const valve::ValveCall &call) override { + if (call.get_position().has_value()) { + this->position = *call.get_position(); + this->publish_state(); + return; + } else if (call.get_toggle().has_value()) { + if (call.get_toggle().value()) { + if (this->position == valve::VALVE_OPEN) { + this->position = valve::VALVE_CLOSED; + this->publish_state(); + } else { + this->position = valve::VALVE_OPEN; + this->publish_state(); + } + } + return; + } else if (call.get_stop()) { + this->current_operation = valve::VALVE_OPERATION_IDLE; + this->publish_state(); // Keep the current position + return; + } + } + DemoValveType type_{}; +}; + +} // namespace demo +} // namespace esphome diff --git a/esphome/components/dfrobot_sen0395/commands.cpp b/esphome/components/dfrobot_sen0395/commands.cpp index 2c60fb3449..42074c80cf 100644 --- a/esphome/components/dfrobot_sen0395/commands.cpp +++ b/esphome/components/dfrobot_sen0395/commands.cpp @@ -21,7 +21,7 @@ uint8_t Command::execute(DfrobotSen0395Component *parent) { if (this->retries_left_ > 0) { this->retries_left_ -= 1; this->cmd_sent_ = false; - ESP_LOGD(TAG, "Retrying..."); + ESP_LOGD(TAG, "Retrying"); return 0; } else { this->parent_->find_prompt_(); @@ -33,7 +33,7 @@ uint8_t Command::execute(DfrobotSen0395Component *parent) { if (this->retries_left_ > 0) { this->retries_left_ -= 1; this->cmd_sent_ = false; - ESP_LOGD(TAG, "Retrying..."); + ESP_LOGD(TAG, "Retrying"); return 0; } else { this->parent_->find_prompt_(); @@ -51,7 +51,7 @@ uint8_t Command::execute(DfrobotSen0395Component *parent) { if (this->retries_left_ > 0) { this->retries_left_ -= 1; this->cmd_sent_ = false; - ESP_LOGD(TAG, "Retrying..."); + ESP_LOGD(TAG, "Retrying"); } else { return 1; // Command done } diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index f2a33a26ac..7248ef624e 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -1,6 +1,6 @@ #include "dht.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace dht { @@ -8,25 +8,19 @@ namespace dht { static const char *const TAG = "dht"; void DHT::setup() { - ESP_LOGCONFIG(TAG, "Setting up DHT..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->pin_->digital_write(true); this->pin_->setup(); this->pin_->digital_write(true); } + void DHT::dump_config() { ESP_LOGCONFIG(TAG, "DHT:"); LOG_PIN(" Pin: ", this->pin_); - if (this->is_auto_detect_) { - ESP_LOGCONFIG(TAG, " Auto-detected model: %s", this->model_ == DHT_MODEL_DHT11 ? "DHT11" : "DHT22"); - } else if (this->model_ == DHT_MODEL_DHT11) { - ESP_LOGCONFIG(TAG, " Model: DHT11"); - } else { - ESP_LOGCONFIG(TAG, " Model: DHT22 (or equivalent)"); - } - ESP_LOGCONFIG(TAG, " Internal Pull-up: %s", ONOFF(this->pin_->get_flags() & gpio::FLAG_PULLUP)); - + ESP_LOGCONFIG(TAG, " %sModel: %s", this->is_auto_detect_ ? "Auto-detected " : "", + this->model_ == DHT_MODEL_DHT11 ? "DHT11" : "DHT22 or equivalent"); + ESP_LOGCONFIG(TAG, " Internal pull-up: %s", ONOFF(this->pin_->get_flags() & gpio::FLAG_PULLUP)); LOG_UPDATE_INTERVAL(this); - LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); } @@ -46,7 +40,7 @@ void DHT::update() { } if (success) { - ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%%", temperature, humidity); + ESP_LOGD(TAG, "Temperature %.1f°C Humidity %.1f%%", temperature, humidity); if (this->temperature_sensor_ != nullptr) this->temperature_sensor_->publish_state(temperature); @@ -54,11 +48,8 @@ void DHT::update() { this->humidity_sensor_->publish_state(humidity); this->status_clear_warning(); } else { - const char *str = ""; - if (this->is_auto_detect_) { - str = " and consider manually specifying the DHT model using the model option"; - } - ESP_LOGW(TAG, "Invalid readings! Please check your wiring (pull-up resistor, pin number)%s.", str); + ESP_LOGW(TAG, "Invalid readings! Check pin number and pull-up resistor%s.", + this->is_auto_detect_ ? " and try manually specifying the model" : ""); if (this->temperature_sensor_ != nullptr) this->temperature_sensor_->publish_state(NAN); if (this->humidity_sensor_ != nullptr) @@ -68,10 +59,12 @@ void DHT::update() { } float DHT::get_setup_priority() const { return setup_priority::DATA; } + void DHT::set_dht_model(DHTModel model) { this->model_ = model; this->is_auto_detect_ = model == DHT_MODEL_AUTO_DETECT; } + bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) { *humidity = NAN; *temperature = NAN; @@ -121,9 +114,9 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r while (!this->pin_->digital_read()) { if (micros() - start_time > 90) { if (i < 0) { - error_code = 1; + error_code = 1; // line didn't clear } else { - error_code = 2; + error_code = 2; // rising edge for bit i timeout } break; } @@ -139,9 +132,9 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r end_time = micros(); if (end_time - start_time > 90) { if (i < 0) { - error_code = 3; + error_code = 3; // requesting data failed } else { - error_code = 4; + error_code = 4; // falling edge for bit i timeout } break; } @@ -166,22 +159,9 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r if (!report_errors && error_code != 0) return false; - switch (error_code) { - case 1: - ESP_LOGW(TAG, "Waiting for DHT communication to clear failed!"); - return false; - case 2: - ESP_LOGW(TAG, "Rising edge for bit %d failed!", i); - return false; - case 3: - ESP_LOGW(TAG, "Requesting data from DHT failed!"); - return false; - case 4: - ESP_LOGW(TAG, "Falling edge for bit %d failed!", i); - return false; - case 0: - default: - break; + if (error_code) { + ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); + return false; } ESP_LOGVV(TAG, @@ -206,15 +186,15 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r if (checksum_a == data[4]) { // Data format: 8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data + 8bit // check sum - some models always have 0 in the decimal part - const uint16_t raw_temperature = uint16_t(data[2]) * 10 + (data[3] & 0x7F); - *temperature = raw_temperature / 10.0f; + const uint16_t raw_temperature = static_cast(data[2]) * 10 + (data[3] & 0x7F); + *temperature = static_cast(raw_temperature) / 10.0f; if ((data[3] & 0x80) != 0) { // negative *temperature *= -1; } - const uint16_t raw_humidity = uint16_t(data[0]) * 10 + data[1]; - *humidity = raw_humidity / 10.0f; + const uint16_t raw_humidity = static_cast(data[0]) * 10 + data[1]; + *humidity = static_cast(raw_humidity) / 10.0f; } else { // For compatibility with DHT11 models which might only use 2 bytes checksums, only use the data from these two // bytes @@ -222,8 +202,8 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r *humidity = data[0]; } } else { - uint16_t raw_humidity = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF); - uint16_t raw_temperature = (uint16_t(data[2] & 0xFF) << 8) | (data[3] & 0xFF); + uint16_t raw_humidity = encode_uint16(data[0], data[1]); + uint16_t raw_temperature = encode_uint16(data[2], data[3]); if (raw_temperature & 0x8000) { if (!(raw_temperature & 0x4000)) @@ -234,24 +214,23 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r if (raw_temperature == 1 && raw_humidity == 10) { if (report_errors) { - ESP_LOGW(TAG, "Invalid temperature+humidity! Sensor reported 1°C and 1%% Hum"); + ESP_LOGW(TAG, "Invalid data"); } return false; } - *humidity = raw_humidity * 0.1f; - if (*humidity > 100) + *humidity = static_cast(raw_humidity) * 0.1f; + if (*humidity > 100.0f) *humidity = NAN; - *temperature = int16_t(raw_temperature) * 0.1f; + *temperature = static_cast(raw_temperature) * 0.1f; } if (*temperature == 0.0f && (*humidity == 1.0f || *humidity == 2.0f)) { if (report_errors) { - ESP_LOGW(TAG, "DHT reports invalid data. Is the update interval too high or the sensor damaged?"); + ESP_LOGW(TAG, "Invalid data"); } return false; } - return true; } diff --git a/esphome/components/dht12/dht12.cpp b/esphome/components/dht12/dht12.cpp index a5e1886918..54a6688b0b 100644 --- a/esphome/components/dht12/dht12.cpp +++ b/esphome/components/dht12/dht12.cpp @@ -34,7 +34,7 @@ void DHT12Component::update() { this->status_clear_warning(); } void DHT12Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up DHT12..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t data[5]; if (!this->read_data_(data)) { this->mark_failed(); @@ -45,7 +45,7 @@ void DHT12Component::dump_config() { ESP_LOGD(TAG, "DHT12:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with DHT12 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); diff --git a/esphome/components/display/display.h b/esphome/components/display/display.h index 43da08f4ac..68c1184721 100644 --- a/esphome/components/display/display.h +++ b/esphome/components/display/display.h @@ -182,9 +182,11 @@ using display_writer_t = std::function; #define LOG_DISPLAY(prefix, type, obj) \ if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, prefix type); \ - ESP_LOGCONFIG(TAG, "%s Rotations: %d °", prefix, (obj)->rotation_); \ - ESP_LOGCONFIG(TAG, "%s Dimensions: %dpx x %dpx", prefix, (obj)->get_width(), (obj)->get_height()); \ + ESP_LOGCONFIG(TAG, \ + prefix type "\n" \ + "%s Rotations: %d °\n" \ + "%s Dimensions: %dpx x %dpx", \ + prefix, (obj)->rotation_, prefix, (obj)->get_width(), (obj)->get_height()); \ } /// Turn the pixel OFF. diff --git a/esphome/components/dps310/dps310.cpp b/esphome/components/dps310/dps310.cpp index 22fb52967f..a7fb7ecd5e 100644 --- a/esphome/components/dps310/dps310.cpp +++ b/esphome/components/dps310/dps310.cpp @@ -12,7 +12,7 @@ void DPS310Component::setup() { auto timer = DPS310_INIT_TIMEOUT; uint8_t reg = 0; - ESP_LOGCONFIG(TAG, "Setting up DPS310..."); + ESP_LOGCONFIG(TAG, "Running setup"); // first, reset the sensor if (!this->write_byte(DPS310_REG_RESET, DPS310_CMD_RESET)) { this->mark_failed(); @@ -86,12 +86,14 @@ void DPS310Component::setup() { } void DPS310Component::dump_config() { - ESP_LOGCONFIG(TAG, "DPS310:"); - ESP_LOGCONFIG(TAG, " Product ID: %u", this->prod_rev_id_ & 0x0F); - ESP_LOGCONFIG(TAG, " Revision ID: %u", (this->prod_rev_id_ >> 4) & 0x0F); + ESP_LOGCONFIG(TAG, + "DPS310:\n" + " Product ID: %u\n" + " Revision ID: %u", + this->prod_rev_id_ & 0x0F, (this->prod_rev_id_ >> 4) & 0x0F); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with DPS310 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); diff --git a/esphome/components/ds1307/ds1307.cpp b/esphome/components/ds1307/ds1307.cpp index 9df8a1d373..db0180e6f1 100644 --- a/esphome/components/ds1307/ds1307.cpp +++ b/esphome/components/ds1307/ds1307.cpp @@ -10,7 +10,7 @@ namespace ds1307 { static const char *const TAG = "ds1307"; void DS1307Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up DS1307..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->read_rtc_()) { this->mark_failed(); } @@ -22,7 +22,7 @@ void DS1307Component::dump_config() { ESP_LOGCONFIG(TAG, "DS1307:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with DS1307 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); } diff --git a/esphome/components/dsmr/dsmr.cpp b/esphome/components/dsmr/dsmr.cpp index c0a2883d79..d99cf5e7a9 100644 --- a/esphome/components/dsmr/dsmr.cpp +++ b/esphome/components/dsmr/dsmr.cpp @@ -278,9 +278,11 @@ bool Dsmr::parse_telegram() { } void Dsmr::dump_config() { - ESP_LOGCONFIG(TAG, "DSMR:"); - ESP_LOGCONFIG(TAG, " Max telegram length: %d", this->max_telegram_len_); - ESP_LOGCONFIG(TAG, " Receive timeout: %.1fs", this->receive_timeout_ / 1e3f); + ESP_LOGCONFIG(TAG, + "DSMR:\n" + " Max telegram length: %d\n" + " Receive timeout: %.1fs", + this->max_telegram_len_, this->receive_timeout_ / 1e3f); if (this->request_pin_ != nullptr) { LOG_PIN(" Request Pin: ", this->request_pin_); } diff --git a/esphome/components/duty_cycle/duty_cycle_sensor.cpp b/esphome/components/duty_cycle/duty_cycle_sensor.cpp index 9a881c81f0..8939de0ee9 100644 --- a/esphome/components/duty_cycle/duty_cycle_sensor.cpp +++ b/esphome/components/duty_cycle/duty_cycle_sensor.cpp @@ -1,6 +1,6 @@ #include "duty_cycle_sensor.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace duty_cycle { @@ -8,7 +8,7 @@ namespace duty_cycle { static const char *const TAG = "duty_cycle"; void DutyCycleSensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up Duty Cycle Sensor '%s'...", this->get_name().c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str()); this->pin_->setup(); this->store_.pin = this->pin_->to_isr(); this->store_.last_level = this->pin_->digital_read(); diff --git a/esphome/components/duty_time/duty_time_sensor.cpp b/esphome/components/duty_time/duty_time_sensor.cpp index d4369c89c0..c7319f7c33 100644 --- a/esphome/components/duty_time/duty_time_sensor.cpp +++ b/esphome/components/duty_time/duty_time_sensor.cpp @@ -94,9 +94,11 @@ void DutyTimeSensor::publish_and_save_(const uint32_t sec, const uint32_t ms) { } void DutyTimeSensor::dump_config() { - ESP_LOGCONFIG(TAG, "Duty Time:"); - ESP_LOGCONFIG(TAG, " Update Interval: %" PRId32 "ms", this->get_update_interval()); - ESP_LOGCONFIG(TAG, " Restore: %s", ONOFF(this->restore_)); + ESP_LOGCONFIG(TAG, + "Duty Time:\n" + " Update Interval: %" PRId32 "ms\n" + " Restore: %s", + this->get_update_interval(), ONOFF(this->restore_)); LOG_SENSOR(" ", "Duty Time Sensor:", this); LOG_SENSOR(" ", "Last Duty Time Sensor:", this->last_duty_time_sensor_); } diff --git a/esphome/components/ee895/ee895.cpp b/esphome/components/ee895/ee895.cpp index a7186ffbbc..bdaa3f3200 100644 --- a/esphome/components/ee895/ee895.cpp +++ b/esphome/components/ee895/ee895.cpp @@ -1,6 +1,6 @@ #include "ee895.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ee895 { @@ -16,7 +16,7 @@ static const uint16_t PRESSURE_ADDRESS = 0x04B0; void EE895Component::setup() { uint16_t crc16_check = 0; - ESP_LOGCONFIG(TAG, "Setting up EE895..."); + ESP_LOGCONFIG(TAG, "Running setup"); write_command_(SERIAL_NUMBER, 8); uint8_t serial_number[20]; this->read(serial_number, 20); @@ -35,7 +35,7 @@ void EE895Component::dump_config() { LOG_I2C_DEVICE(this); switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Communication with EE895 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case CRC_CHECK_FAILED: ESP_LOGE(TAG, "The crc check failed"); diff --git a/esphome/components/ektf2232/touchscreen/ektf2232.cpp b/esphome/components/ektf2232/touchscreen/ektf2232.cpp index 603b554273..666e56e2a7 100644 --- a/esphome/components/ektf2232/touchscreen/ektf2232.cpp +++ b/esphome/components/ektf2232/touchscreen/ektf2232.cpp @@ -16,7 +16,7 @@ static const uint8_t GET_Y_RES[4] = {0x53, 0x63, 0x00, 0x00}; static const uint8_t GET_POWER_STATE_CMD[4] = {0x53, 0x50, 0x00, 0x01}; void EKTF2232Touchscreen::setup() { - ESP_LOGCONFIG(TAG, "Setting up EKT2232 Touchscreen..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); this->interrupt_pin_->setup(); diff --git a/esphome/components/emc2101/emc2101.cpp b/esphome/components/emc2101/emc2101.cpp index 2f45b2e27a..75d324c2bb 100644 --- a/esphome/components/emc2101/emc2101.cpp +++ b/esphome/components/emc2101/emc2101.cpp @@ -57,7 +57,7 @@ static const uint8_t EMC2101_POLARITY_BIT = 1 << 4; float Emc2101Component::get_setup_priority() const { return setup_priority::HARDWARE; } void Emc2101Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up Emc2101 sensor..."); + ESP_LOGCONFIG(TAG, "Running setup"); // make sure we're talking to the right chip uint8_t chip_id = reg(EMC2101_REGISTER_WHOAMI).get(); @@ -94,14 +94,16 @@ void Emc2101Component::dump_config() { ESP_LOGCONFIG(TAG, "Emc2101 component:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with EMC2101 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } ESP_LOGCONFIG(TAG, " Mode: %s", this->dac_mode_ ? "DAC" : "PWM"); if (this->dac_mode_) { ESP_LOGCONFIG(TAG, " DAC Conversion Rate: %X", this->dac_conversion_rate_); } else { - ESP_LOGCONFIG(TAG, " PWM Resolution: %02X", this->pwm_resolution_); - ESP_LOGCONFIG(TAG, " PWM Divider: %02X", this->pwm_divider_); + ESP_LOGCONFIG(TAG, + " PWM Resolution: %02X\n" + " PWM Divider: %02X", + this->pwm_resolution_, this->pwm_divider_); } ESP_LOGCONFIG(TAG, " Inverted: %s", YESNO(this->inverted_)); } @@ -110,7 +112,7 @@ void Emc2101Component::set_duty_cycle(float value) { uint8_t duty_cycle = remap(value, 0.0f, 1.0f, (uint8_t) 0, this->max_output_value_); ESP_LOGD(TAG, "Setting duty fan setting to %02X", duty_cycle); if (!this->write_byte(EMC2101_REGISTER_FAN_SETTING, duty_cycle)) { - ESP_LOGE(TAG, "Communication with EMC2101 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning(); return; } @@ -119,7 +121,7 @@ void Emc2101Component::set_duty_cycle(float value) { float Emc2101Component::get_duty_cycle() { uint8_t duty_cycle; if (!this->read_byte(EMC2101_REGISTER_FAN_SETTING, &duty_cycle)) { - ESP_LOGE(TAG, "Communication with EMC2101 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning(); return NAN; } @@ -129,7 +131,7 @@ float Emc2101Component::get_duty_cycle() { float Emc2101Component::get_internal_temperature() { uint8_t temperature; if (!this->read_byte(EMC2101_REGISTER_INTERNAL_TEMP, &temperature)) { - ESP_LOGE(TAG, "Communication with EMC2101 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning(); return NAN; } @@ -141,7 +143,7 @@ float Emc2101Component::get_external_temperature() { uint8_t lsb, msb; if (!this->read_byte(EMC2101_REGISTER_EXTERNAL_TEMP_MSB, &msb) || !this->read_byte(EMC2101_REGISTER_EXTERNAL_TEMP_LSB, &lsb)) { - ESP_LOGE(TAG, "Communication with EMC2101 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning(); return NAN; } @@ -155,7 +157,7 @@ float Emc2101Component::get_speed() { // Read **LSB** first to match 'Data Read Interlock' behavior from 6.1 of datasheet uint8_t lsb, msb; if (!this->read_byte(EMC2101_REGISTER_TACH_LSB, &lsb) || !this->read_byte(EMC2101_REGISTER_TACH_MSB, &msb)) { - ESP_LOGE(TAG, "Communication with EMC2101 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning(); return NAN; } diff --git a/esphome/components/ens160_base/ens160_base.cpp b/esphome/components/ens160_base/ens160_base.cpp index 852328d4bb..7e5b8528b7 100644 --- a/esphome/components/ens160_base/ens160_base.cpp +++ b/esphome/components/ens160_base/ens160_base.cpp @@ -49,7 +49,7 @@ static const uint8_t ENS160_DATA_STATUS_NEWGPR = 0x01; static const uint8_t ENS160_DATA_AQI = 0x07; void ENS160Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up ENS160..."); + ESP_LOGCONFIG(TAG, "Running setup"); // check part_id uint16_t part_id; @@ -279,7 +279,7 @@ void ENS160Component::dump_config() { switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Communication failed! Is the sensor connected?"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case READ_FAILED: ESP_LOGE(TAG, "Error reading from register"); diff --git a/esphome/components/ens210/ens210.cpp b/esphome/components/ens210/ens210.cpp index 86890c05e8..b296e9dd42 100644 --- a/esphome/components/ens210/ens210.cpp +++ b/esphome/components/ens210/ens210.cpp @@ -87,7 +87,7 @@ static uint32_t crc7(uint32_t value) { } void ENS210Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up ENS210..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t data[2]; uint16_t part_id = 0; // Reset @@ -163,7 +163,7 @@ void ENS210Component::update() { // Read T_VAL and H_VAL if (!this->read_bytes(ENS210_REGISTER_T_VAL, data, 6)) { - ESP_LOGE(TAG, "Communication with ENS210 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning(); return; } diff --git a/esphome/components/es7210/es7210.cpp b/esphome/components/es7210/es7210.cpp index 9a99e60995..bcbaf3d270 100644 --- a/esphome/components/es7210/es7210.cpp +++ b/esphome/components/es7210/es7210.cpp @@ -25,9 +25,11 @@ static const size_t MCLK_DIV_FRE = 256; } void ES7210::dump_config() { - ESP_LOGCONFIG(TAG, "ES7210 audio ADC:"); - ESP_LOGCONFIG(TAG, " Bits Per Sample: %" PRIu8, this->bits_per_sample_); - ESP_LOGCONFIG(TAG, " Sample Rate: %" PRIu32, this->sample_rate_); + ESP_LOGCONFIG(TAG, + "ES7210 audio ADC:\n" + " Bits Per Sample: %" PRIu8 "\n" + " Sample Rate: %" PRIu32, + this->bits_per_sample_, this->sample_rate_); if (this->is_failed()) { ESP_LOGE(TAG, " Failed to initialize"); @@ -36,7 +38,7 @@ void ES7210::dump_config() { } void ES7210::setup() { - ESP_LOGCONFIG(TAG, "Setting up ES7210..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Software reset ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0xff)); diff --git a/esphome/components/es7243e/es7243e.cpp b/esphome/components/es7243e/es7243e.cpp index ce65ce973e..d5115cb880 100644 --- a/esphome/components/es7243e/es7243e.cpp +++ b/esphome/components/es7243e/es7243e.cpp @@ -34,7 +34,7 @@ void ES7243E::dump_config() { } void ES7243E::setup() { - ESP_LOGCONFIG(TAG, "Setting up ES7243E..."); + ESP_LOGCONFIG(TAG, "Running setup"); ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A)); ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80)); diff --git a/esphome/components/es8156/es8156.cpp b/esphome/components/es8156/es8156.cpp index 62d35aa2d2..c8330b4f84 100644 --- a/esphome/components/es8156/es8156.cpp +++ b/esphome/components/es8156/es8156.cpp @@ -17,7 +17,7 @@ static const char *const TAG = "es8156"; } void ES8156::setup() { - ESP_LOGCONFIG(TAG, "Setting up ES8156..."); + ESP_LOGCONFIG(TAG, "Running setup"); ES8156_ERROR_FAILED(this->write_byte(ES8156_REG02_SCLK_MODE, 0x04)); ES8156_ERROR_FAILED(this->write_byte(ES8156_REG20_ANALOG_SYS1, 0x2A)); diff --git a/esphome/components/es8311/es8311.cpp b/esphome/components/es8311/es8311.cpp index 1cb1fbbe08..0e59ac12d5 100644 --- a/esphome/components/es8311/es8311.cpp +++ b/esphome/components/es8311/es8311.cpp @@ -22,7 +22,7 @@ static const char *const TAG = "es8311"; } void ES8311::setup() { - ESP_LOGCONFIG(TAG, "Setting up ES8311..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Reset ES8311_ERROR_FAILED(this->write_byte(ES8311_REG00_RESET, 0x1F)); @@ -52,11 +52,13 @@ void ES8311::setup() { } void ES8311::dump_config() { - ESP_LOGCONFIG(TAG, "ES8311 Audio Codec:"); - ESP_LOGCONFIG(TAG, " Use MCLK: %s", YESNO(this->use_mclk_)); - ESP_LOGCONFIG(TAG, " Use Microphone: %s", YESNO(this->use_mic_)); - ESP_LOGCONFIG(TAG, " DAC Bits per Sample: %" PRIu8, this->resolution_out_); - ESP_LOGCONFIG(TAG, " Sample Rate: %" PRIu32, this->sample_frequency_); + ESP_LOGCONFIG(TAG, + "ES8311 Audio Codec:\n" + " Use MCLK: %s\n" + " Use Microphone: %s\n" + " DAC Bits per Sample: %" PRIu8 "\n" + " Sample Rate: %" PRIu32, + YESNO(this->use_mclk_), YESNO(this->use_mic_), this->resolution_out_, this->sample_frequency_); if (this->is_failed()) { ESP_LOGCONFIG(TAG, " Failed to initialize!"); diff --git a/esphome/components/es8388/__init__.py b/esphome/components/es8388/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/es8388/audio_dac.py b/esphome/components/es8388/audio_dac.py new file mode 100644 index 0000000000..77e07b2e01 --- /dev/null +++ b/esphome/components/es8388/audio_dac.py @@ -0,0 +1,26 @@ +import esphome.codegen as cg +from esphome.components import i2c +from esphome.components.audio_dac import AudioDac +import esphome.config_validation as cv +from esphome.const import CONF_ID + +CODEOWNERS = ["@P4uLT"] +CONF_ES8388_ID = "es8388_id" + +es8388_ns = cg.esphome_ns.namespace("es8388") + +ES8388 = es8388_ns.class_("ES8388", AudioDac, cg.Component, i2c.I2CDevice) + +DEPENDENCIES = ["i2c"] + +CONFIG_SCHEMA = ( + cv.Schema({cv.GenerateID(): cv.declare_id(ES8388)}) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x10)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) diff --git a/esphome/components/es8388/es8388.cpp b/esphome/components/es8388/es8388.cpp new file mode 100644 index 0000000000..87cf9a47ee --- /dev/null +++ b/esphome/components/es8388/es8388.cpp @@ -0,0 +1,289 @@ +#include "es8388.h" + +#include +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace es8388 { + +static const char *const TAG = "es8388"; + +// Mark the component as failed; use only in setup +#define ES8388_ERROR_FAILED(func) \ + if (!(func)) { \ + this->mark_failed(); \ + return; \ + } + +// Return false; use outside of setup +#define ES8388_ERROR_CHECK(func) \ + if (!(func)) { \ + return false; \ + } + +void ES8388::setup() { + ESP_LOGCONFIG(TAG, "Running setup"); + + // mute DAC + this->set_mute_state_(true); + + // I2S worker mode + ES8388_ERROR_FAILED(this->write_byte(ES8388_MASTERMODE, 0x00)); + + /* Chip Control and Power Management */ + ES8388_ERROR_FAILED(this->write_byte(ES8388_CONTROL2, 0x50)); + // normal all and power up all + ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0x00)); + + // vmidsel/500k + // EnRef=0,Play&Record Mode,(0x17-both of mic&play) + ES8388_ERROR_FAILED(this->write_byte(ES8388_CONTROL1, 0x12)); + + // i2s 16 bits + ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL1, 0x18)); + // sample freq 256 + // DACFsMode,SINGLE SPEED; DACFsRatio,256 + ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL2, 0x02)); + // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2 + ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL16, 0x00)); + // only left DAC to left mixer enable 0db + ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL17, 0x90)); + // only right DAC to right mixer enable 0db + ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL20, 0x90)); + // set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK + ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL21, 0x80)); + // vroi=0 - 1.5k VREF to analog output resistance (default) + ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL23, 0x00)); + + // power down adc and line in + ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCPOWER, 0xFF)); + + //@nightdav + ES8388_ERROR_FAILED( + this->write_byte(ES8388_ADCCONTROL1, 0x00)); // +21dB : recommended value for ALC & voice recording + + // set to Mono Right + ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL3, 0x02)); + + // I2S 16 Bits length and I2S serial audio data format + ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL4, 0x0d)); + // ADCFsMode,singel SPEED,RATIO=256 + ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL5, 0x02)); + + // ADC Volume + ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL8, 0x00)); + ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL9, 0x00)); + + //@nightDav + // ALC Config (as recommended by ES8388 user guide for voice recording) + + // Reg 0x12 = 0xe2 (ALC enable, PGA Max. Gain=23.5dB, Min. Gain=0dB) + ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL10, 0xe2)); + + // Reg 0x13 = 0xa0 (ALC Target=-1.5dB, ALC Hold time =0 mS) + ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL11, 0xa0)); + // Reg 0x14 = 0x12(Decay time =820uS , Attack time = 416 uS) + ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL12, 0x12)); + + // Reg 0x15 = 0x06(ALC mode) + ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL13, 0x06)); + + // Reg 0x16 = 0xc3(nose gate = -40.5dB, NGG = 0x01(mute ADC)) + ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL14, 0xc3)); + + // Power on ADC + ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL21, 0x80)); + + // Start state machine + ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0xF0)); + delay(1); + ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0x00)); + + // DAC volume max + // Set initial volume + // this->set_volume(0.75); // 0.75 = 0xBF = 0dB + + this->set_mute_state_(false); + + // unmute ADC with fade in + ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL7, 0x60)); + // unmute DAC with fade in + ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL3, 0x20)); + + // Power on ADC, Enable LIN&RIN, Power off MICBIAS, set int1lp to low power mode + ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCPOWER, 0x09)); + +#ifdef USE_SELECT + if (this->dac_output_select_ != nullptr) { + auto dac_power = this->get_dac_power(); + if (dac_power.has_value()) { + auto dac_power_str = this->dac_output_select_->at(dac_power.value()); + if (dac_power_str.has_value()) { + this->dac_output_select_->publish_state(dac_power_str.value()); + } else { + ESP_LOGW(TAG, "Unknown DAC output power value: %d", dac_power.value()); + } + } + } + if (this->adc_input_mic_select_ != nullptr) { + auto mic_input = this->get_mic_input(); + if (mic_input.has_value()) { + auto mic_input_str = this->adc_input_mic_select_->at(mic_input.value()); + if (mic_input_str.has_value()) { + this->adc_input_mic_select_->publish_state(mic_input_str.value()); + } else { + ESP_LOGW(TAG, "Unknown ADC input mic value: %d", mic_input.value()); + } + } + } +#endif +} + +void ES8388::dump_config() { + ESP_LOGCONFIG(TAG, "ES8388 Audio Codec:"); + LOG_I2C_DEVICE(this); +#ifdef USE_SELECT + LOG_SELECT(" ", "DacOutputSelect", this->dac_output_select_); + LOG_SELECT(" ", "ADCInputMicSelect", this->adc_input_mic_select_); +#endif + + if (this->is_failed()) { + ESP_LOGCONFIG(TAG, " Failed to initialize"); + return; + } +} + +bool ES8388::set_volume(float volume) { + volume = clamp(volume, 0.0f, 1.0f); + uint8_t value = remap(volume, 0.0f, 1.0f, -96, 0); + ESP_LOGD(TAG, "Setting ES8388_DACCONTROL4 / ES8388_DACCONTROL5 to 0x%02X (volume: %f)", value, volume); + ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL4, value)); + ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL5, value)); + + return true; +} + +float ES8388::volume() { + uint8_t value; + ES8388_ERROR_CHECK(this->read_byte(ES8388_DACCONTROL4, &value)); + return remap(value, -96, 0, 0.0f, 1.0f); +} + +bool ES8388::set_mute_state_(bool mute_state) { + uint8_t value = 0; + + this->is_muted_ = mute_state; + + ES8388_ERROR_CHECK(this->read_byte(ES8388_DACCONTROL3, &value)); + ESP_LOGV(TAG, "Read ES8388_DACCONTROL3: 0x%02X", value); + + if (mute_state) { + value = 0x3C; + } + + ESP_LOGV(TAG, "Setting ES8388_DACCONTROL3 to 0x%02X (muted: %s)", value, YESNO(mute_state)); + return this->write_byte(ES8388_DACCONTROL3, value); +} + +// Set dac power output +bool ES8388::set_dac_output(DacOutputLine line) { + uint8_t reg_out1 = 0; + uint8_t reg_out2 = 0; + uint8_t dac_power = 0; + + // 0x00: -30dB , 0x1E: 0dB + switch (line) { + case DAC_OUTPUT_LINE1: + reg_out1 = 0x1E; + dac_power = ES8388_DAC_OUTPUT_LOUT1_ROUT1; + break; + case DAC_OUTPUT_LINE2: + reg_out2 = 0x1E; + dac_power = ES8388_DAC_OUTPUT_LOUT2_ROUT2; + break; + case DAC_OUTPUT_BOTH: + reg_out1 = 0x1E; + reg_out2 = 0x1E; + dac_power = ES8388_DAC_OUTPUT_BOTH; + break; + default: + ESP_LOGE(TAG, "Unknown DAC output line: %d", line); + return false; + }; + + ESP_LOGV(TAG, "Setting ES8388_DACPOWER to 0x%02X", dac_power); + ESP_LOGV(TAG, "Setting ES8388_DACCONTROL24 / ES8388_DACCONTROL25 to 0x%02X", reg_out1); + ESP_LOGV(TAG, "Setting ES8388_DACCONTROL26 / ES8388_DACCONTROL27 to 0x%02X", reg_out2); + + ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL24, reg_out1)); // LOUT1VOL + ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL25, reg_out1)); // ROUT1VOL + ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL26, reg_out2)); // LOUT2VOL + ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL27, reg_out2)); // ROUT1VOL + + return this->write_byte(ES8388_DACPOWER, dac_power); +} + +optional ES8388::get_dac_power() { + uint8_t dac_power; + if (!this->read_byte(ES8388_DACPOWER, &dac_power)) { + this->status_momentary_warning("Failed to read ES8388_DACPOWER"); + return {}; + } + switch (dac_power) { + case ES8388_DAC_OUTPUT_LOUT1_ROUT1: + return DAC_OUTPUT_LINE1; + case ES8388_DAC_OUTPUT_LOUT2_ROUT2: + return DAC_OUTPUT_LINE2; + case ES8388_DAC_OUTPUT_BOTH: + return DAC_OUTPUT_BOTH; + default: + return {}; + } +} + +// Set ADC input MIC +bool ES8388::set_adc_input_mic(AdcInputMicLine line) { + uint8_t mic_input = 0; + + switch (line) { + case ADC_INPUT_MIC_LINE1: + mic_input = ES8388_ADC_INPUT_LINPUT1_RINPUT1; + break; + case ADC_INPUT_MIC_LINE2: + mic_input = ES8388_ADC_INPUT_LINPUT2_RINPUT2; + break; + case ADC_INPUT_MIC_DIFFERENCE: + mic_input = ES8388_ADC_INPUT_DIFFERENCE; + break; + default: + ESP_LOGE(TAG, "Unknown ADC input mic line: %d", line); + return false; + } + + ESP_LOGV(TAG, "Setting ES8388_ADCCONTROL2 to 0x%02X", mic_input); + ES8388_ERROR_CHECK(this->write_byte(ES8388_ADCCONTROL2, mic_input)); + + return true; +} + +optional ES8388::get_mic_input() { + uint8_t mic_input; + if (!this->read_byte(ES8388_ADCCONTROL2, &mic_input)) { + this->status_momentary_warning("Failed to read ES8388_ADCCONTROL2"); + return {}; + } + switch (mic_input) { + case ES8388_ADC_INPUT_LINPUT1_RINPUT1: + return ADC_INPUT_MIC_LINE1; + case ES8388_ADC_INPUT_LINPUT2_RINPUT2: + return ADC_INPUT_MIC_LINE2; + case ES8388_ADC_INPUT_DIFFERENCE: + return ADC_INPUT_MIC_DIFFERENCE; + default: + return {}; + }; +} + +} // namespace es8388 +} // namespace esphome diff --git a/esphome/components/es8388/es8388.h b/esphome/components/es8388/es8388.h new file mode 100644 index 0000000000..45944f68bd --- /dev/null +++ b/esphome/components/es8388/es8388.h @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include "esphome/components/audio_dac/audio_dac.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/core/component.h" +#ifdef USE_SELECT +#include "esphome/components/select/select.h" +#endif + +#include "es8388_const.h" + +namespace esphome { +namespace es8388 { + +enum DacOutputLine : uint8_t { + DAC_OUTPUT_LINE1, + DAC_OUTPUT_LINE2, + DAC_OUTPUT_BOTH, +}; + +enum AdcInputMicLine : uint8_t { + ADC_INPUT_MIC_LINE1, + ADC_INPUT_MIC_LINE2, + ADC_INPUT_MIC_DIFFERENCE, +}; + +class ES8388 : public audio_dac::AudioDac, public Component, public i2c::I2CDevice { +#ifdef USE_SELECT + SUB_SELECT(dac_output) + SUB_SELECT(adc_input_mic) +#endif + + public: + ///////////////////////// + // Component overrides // + ///////////////////////// + + void setup() override; + float get_setup_priority() const override { return setup_priority::DATA; } + void dump_config() override; + + //////////////////////// + // AudioDac overrides // + //////////////////////// + + /// @brief Writes the volume out to the DAC + /// @param volume floating point between 0.0 and 1.0 + /// @return True if successful and false otherwise + bool set_volume(float volume) override; + + /// @brief Gets the current volume out from the DAC + /// @return floating point between 0.0 and 1.0 + float volume() override; + + /// @brief Disables mute for audio out + /// @return True if successful and false otherwise + bool set_mute_off() override { return this->set_mute_state_(false); } + + /// @brief Enables mute for audio out + /// @return True if successful and false otherwise + bool set_mute_on() override { return this->set_mute_state_(true); } + + bool is_muted() override { return this->is_muted_; } + + optional get_dac_power(); + optional get_mic_input(); + + bool set_dac_output(DacOutputLine line); + bool set_adc_input_mic(AdcInputMicLine line); + + protected: + /// @brief Mutes or unmutes the DAC audio out + /// @param mute_state True to mute, false to unmute + /// @return True if successful and false otherwise + bool set_mute_state_(bool mute_state); +}; + +} // namespace es8388 +} // namespace esphome diff --git a/esphome/components/es8388/es8388_const.h b/esphome/components/es8388/es8388_const.h new file mode 100644 index 0000000000..2a51f078bc --- /dev/null +++ b/esphome/components/es8388/es8388_const.h @@ -0,0 +1,83 @@ +#pragma once +#include + +namespace esphome { +namespace es8388 { + +/* ES8388 register */ +static const uint8_t ES8388_CONTROL1 = 0x00; +static const uint8_t ES8388_CONTROL2 = 0x01; + +static const uint8_t ES8388_CHIPPOWER = 0x02; + +static const uint8_t ES8388_ADCPOWER = 0x03; +static const uint8_t ES8388_DACPOWER = 0x04; + +static const uint8_t ES8388_CHIPLOPOW1 = 0x05; +static const uint8_t ES8388_CHIPLOPOW2 = 0x06; + +static const uint8_t ES8388_ANAVOLMANAG = 0x07; + +static const uint8_t ES8388_MASTERMODE = 0x08; + +/* ADC */ +static const uint8_t ES8388_ADCCONTROL1 = 0x09; +static const uint8_t ES8388_ADCCONTROL2 = 0x0a; +static const uint8_t ES8388_ADCCONTROL3 = 0x0b; +static const uint8_t ES8388_ADCCONTROL4 = 0x0c; +static const uint8_t ES8388_ADCCONTROL5 = 0x0d; +static const uint8_t ES8388_ADCCONTROL6 = 0x0e; +static const uint8_t ES8388_ADCCONTROL7 = 0x0f; +static const uint8_t ES8388_ADCCONTROL8 = 0x10; +static const uint8_t ES8388_ADCCONTROL9 = 0x11; +static const uint8_t ES8388_ADCCONTROL10 = 0x12; +static const uint8_t ES8388_ADCCONTROL11 = 0x13; +static const uint8_t ES8388_ADCCONTROL12 = 0x14; +static const uint8_t ES8388_ADCCONTROL13 = 0x15; +static const uint8_t ES8388_ADCCONTROL14 = 0x16; +/* DAC */ +static const uint8_t ES8388_DACCONTROL1 = 0x17; +static const uint8_t ES8388_DACCONTROL2 = 0x18; +static const uint8_t ES8388_DACCONTROL3 = 0x19; +static const uint8_t ES8388_DACCONTROL4 = 0x1a; +static const uint8_t ES8388_DACCONTROL5 = 0x1b; +static const uint8_t ES8388_DACCONTROL6 = 0x1c; +static const uint8_t ES8388_DACCONTROL7 = 0x1d; +static const uint8_t ES8388_DACCONTROL8 = 0x1e; +static const uint8_t ES8388_DACCONTROL9 = 0x1f; +static const uint8_t ES8388_DACCONTROL10 = 0x20; +static const uint8_t ES8388_DACCONTROL11 = 0x21; +static const uint8_t ES8388_DACCONTROL12 = 0x22; +static const uint8_t ES8388_DACCONTROL13 = 0x23; +static const uint8_t ES8388_DACCONTROL14 = 0x24; +static const uint8_t ES8388_DACCONTROL15 = 0x25; +static const uint8_t ES8388_DACCONTROL16 = 0x26; +static const uint8_t ES8388_DACCONTROL17 = 0x27; +static const uint8_t ES8388_DACCONTROL18 = 0x28; +static const uint8_t ES8388_DACCONTROL19 = 0x29; +static const uint8_t ES8388_DACCONTROL20 = 0x2a; +static const uint8_t ES8388_DACCONTROL21 = 0x2b; +static const uint8_t ES8388_DACCONTROL22 = 0x2c; +static const uint8_t ES8388_DACCONTROL23 = 0x2d; +static const uint8_t ES8388_DACCONTROL24 = 0x2e; +static const uint8_t ES8388_DACCONTROL25 = 0x2f; +static const uint8_t ES8388_DACCONTROL26 = 0x30; +static const uint8_t ES8388_DACCONTROL27 = 0x31; +static const uint8_t ES8388_DACCONTROL28 = 0x32; +static const uint8_t ES8388_DACCONTROL29 = 0x33; +static const uint8_t ES8388_DACCONTROL30 = 0x34; + +static const uint8_t ES8388_DAC_OUTPUT_NONE = 0xC0; // ALL DAC DOWN + +static const uint8_t ES8388_DAC_OUTPUT_LOUT1_ROUT1 = 0x30; +static const uint8_t ES8388_DAC_OUTPUT_LOUT2_ROUT2 = 0x0C; +static const uint8_t ES8388_DAC_OUTPUT_BOTH = 0x3C; + +static const uint8_t ES8388_ADC_INPUT_LINPUT1_RINPUT1 = 0x00; +static const uint8_t ES8388_ADC_INPUT_MIC1 = 0x05; +static const uint8_t ES8388_ADC_INPUT_MIC2 = 0x06; +static const uint8_t ES8388_ADC_INPUT_LINPUT2_RINPUT2 = 0x50; +static const uint8_t ES8388_ADC_INPUT_DIFFERENCE = 0xf0; + +} // namespace es8388 +} // namespace esphome diff --git a/esphome/components/es8388/select/__init__.py b/esphome/components/es8388/select/__init__.py new file mode 100644 index 0000000000..068d9f9fb8 --- /dev/null +++ b/esphome/components/es8388/select/__init__.py @@ -0,0 +1,47 @@ +import esphome.codegen as cg +from esphome.components import select +import esphome.config_validation as cv +from esphome.const import ENTITY_CATEGORY_CONFIG, ICON_CHIP # noqa: F401 + +from ..audio_dac import CONF_ES8388_ID, ES8388, es8388_ns + +CONF_DAC_OUTPUT = "dac_output" +CONF_ADC_INPUT_MIC = "adc_input_mic" + +DacOutputSelect = es8388_ns.class_("DacOutputSelect", select.Select) +ADCInputMicSelect = es8388_ns.class_("ADCInputMicSelect", select.Select) + +CONFIG_SCHEMA = cv.All( + { + cv.GenerateID(CONF_ES8388_ID): cv.use_id(ES8388), + cv.Optional(CONF_DAC_OUTPUT): select.select_schema( + DacOutputSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_CHIP, + ), + cv.Optional(CONF_ADC_INPUT_MIC): select.select_schema( + ADCInputMicSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_CHIP, + ), + } +) + + +async def to_code(config): + parent = await cg.get_variable(config[CONF_ES8388_ID]) + if dac_output_config := config.get(CONF_DAC_OUTPUT): + s = await select.new_select( + dac_output_config, + options=["LINE1", "LINE2", "BOTH"], + ) + await cg.register_parented(s, parent) + cg.add(parent.set_dac_output_select(s)) + + if adc_input_mic_config := config.get(CONF_ADC_INPUT_MIC): + s = await select.new_select( + adc_input_mic_config, + options=["LINE1", "LINE2", "DIFFERENCE"], + ) + await cg.register_parented(s, parent) + cg.add(parent.set_adc_input_mic_select(s)) diff --git a/esphome/components/es8388/select/adc_input_mic_select.cpp b/esphome/components/es8388/select/adc_input_mic_select.cpp new file mode 100644 index 0000000000..5fab5b8a92 --- /dev/null +++ b/esphome/components/es8388/select/adc_input_mic_select.cpp @@ -0,0 +1,12 @@ +#include "adc_input_mic_select.h" + +namespace esphome { +namespace es8388 { + +void ADCInputMicSelect::control(const std::string &value) { + this->publish_state(value); + this->parent_->set_adc_input_mic(static_cast(this->index_of(value).value())); +} + +} // namespace es8388 +} // namespace esphome diff --git a/esphome/components/es8388/select/adc_input_mic_select.h b/esphome/components/es8388/select/adc_input_mic_select.h new file mode 100644 index 0000000000..8d035525ef --- /dev/null +++ b/esphome/components/es8388/select/adc_input_mic_select.h @@ -0,0 +1,15 @@ +#pragma once + +#include "esphome/components/es8388/es8388.h" +#include "esphome/components/select/select.h" + +namespace esphome { +namespace es8388 { + +class ADCInputMicSelect : public select::Select, public Parented { + protected: + void control(const std::string &value) override; +}; + +} // namespace es8388 +} // namespace esphome diff --git a/esphome/components/es8388/select/dac_output_select.cpp b/esphome/components/es8388/select/dac_output_select.cpp new file mode 100644 index 0000000000..268b5f290c --- /dev/null +++ b/esphome/components/es8388/select/dac_output_select.cpp @@ -0,0 +1,12 @@ +#include "dac_output_select.h" + +namespace esphome { +namespace es8388 { + +void DacOutputSelect::control(const std::string &value) { + this->publish_state(value); + this->parent_->set_dac_output(static_cast(this->index_of(value).value())); +} + +} // namespace es8388 +} // namespace esphome diff --git a/esphome/components/es8388/select/dac_output_select.h b/esphome/components/es8388/select/dac_output_select.h new file mode 100644 index 0000000000..fccae9fc19 --- /dev/null +++ b/esphome/components/es8388/select/dac_output_select.h @@ -0,0 +1,15 @@ +#pragma once + +#include "esphome/components/es8388/es8388.h" +#include "esphome/components/select/select.h" + +namespace esphome { +namespace es8388 { + +class DacOutputSelect : public select::Select, public Parented { + protected: + void control(const std::string &value) override; +}; + +} // namespace es8388 +} // namespace esphome diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 12d0f9fcd5..157fd9db11 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -3,7 +3,6 @@ import itertools import logging import os from pathlib import Path -from typing import Optional, Union from esphome import git import esphome.codegen as cg @@ -58,8 +57,10 @@ from .const import ( # noqa VARIANT_ESP32, VARIANT_ESP32C2, VARIANT_ESP32C3, + VARIANT_ESP32C5, VARIANT_ESP32C6, VARIANT_ESP32H2, + VARIANT_ESP32P4, VARIANT_ESP32S2, VARIANT_ESP32S3, VARIANT_FRIENDLY, @@ -70,12 +71,35 @@ from .const import ( # noqa from .gpio import esp32_pin_to_code # noqa _LOGGER = logging.getLogger(__name__) -CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["preferences"] +CODEOWNERS = ["@esphome/core"] IS_TARGET_PLATFORM = True -CONF_RELEASE = "release" +CONF_ASSERTION_LEVEL = "assertion_level" +CONF_COMPILER_OPTIMIZATION = "compiler_optimization" CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES = "enable_idf_experimental_features" +CONF_ENABLE_LWIP_ASSERT = "enable_lwip_assert" +CONF_RELEASE = "release" + +ASSERTION_LEVELS = { + "DISABLE": "CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE", + "ENABLE": "CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE", + "SILENT": "CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT", +} + +COMPILER_OPTIMIZATIONS = { + "DEBUG": "CONFIG_COMPILER_OPTIMIZATION_DEBUG", + "NONE": "CONFIG_COMPILER_OPTIMIZATION_NONE", + "PERF": "CONFIG_COMPILER_OPTIMIZATION_PERF", + "SIZE": "CONFIG_COMPILER_OPTIMIZATION_SIZE", +} + +ARDUINO_ALLOWED_VARIANTS = [ + VARIANT_ESP32, + VARIANT_ESP32C3, + VARIANT_ESP32S2, + VARIANT_ESP32S3, +] def get_cpu_frequencies(*frequencies): @@ -88,8 +112,10 @@ CPU_FREQUENCIES = { VARIANT_ESP32S3: get_cpu_frequencies(80, 160, 240), VARIANT_ESP32C2: get_cpu_frequencies(80, 120), VARIANT_ESP32C3: get_cpu_frequencies(80, 160), + VARIANT_ESP32C5: get_cpu_frequencies(80, 160, 240), VARIANT_ESP32C6: get_cpu_frequencies(80, 120, 160), VARIANT_ESP32H2: get_cpu_frequencies(16, 32, 48, 64, 96), + VARIANT_ESP32P4: get_cpu_frequencies(40, 360, 400), } # Make sure not missed here if a new variant added. @@ -124,12 +150,17 @@ def set_core_data(config): CORE.data[KEY_ESP32][KEY_COMPONENTS] = {} elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO: CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino" + if variant not in ARDUINO_ALLOWED_VARIANTS: + raise cv.Invalid( + f"ESPHome does not support using the Arduino framework for the {variant}. Please use the ESP-IDF framework instead.", + path=[CONF_FRAMEWORK, CONF_TYPE], + ) CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse( config[CONF_FRAMEWORK][CONF_VERSION] ) CORE.data[KEY_ESP32][KEY_BOARD] = config[CONF_BOARD] - CORE.data[KEY_ESP32][KEY_VARIANT] = config[CONF_VARIANT] + CORE.data[KEY_ESP32][KEY_VARIANT] = variant CORE.data[KEY_ESP32][KEY_EXTRA_BUILD_FILES] = {} return config @@ -189,7 +220,7 @@ class RawSdkconfigValue: value: str -SdkconfigValueType = Union[bool, int, HexInt, str, RawSdkconfigValue] +SdkconfigValueType = bool | int | HexInt | str | RawSdkconfigValue def add_idf_sdkconfig_option(name: str, value: SdkconfigValueType): @@ -206,8 +237,8 @@ def add_idf_component( ref: str = None, path: str = None, refresh: TimePeriod = None, - components: Optional[list[str]] = None, - submodules: Optional[list[str]] = None, + components: list[str] | None = None, + submodules: list[str] | None = None, ): """Add an esp-idf component to the project.""" if not CORE.using_esp_idf: @@ -296,11 +327,11 @@ ARDUINO_PLATFORM_VERSION = cv.Version(5, 4, 0) # The default/recommended esp-idf framework version # - https://github.com/espressif/esp-idf/releases # - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf -RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(5, 1, 6) +RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(5, 3, 2) # The platformio/espressif32 version to use for esp-idf frameworks # - https://github.com/platformio/platform-espressif32/releases # - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 -ESP_IDF_PLATFORM_VERSION = cv.Version(51, 3, 7) +ESP_IDF_PLATFORM_VERSION = cv.Version(53, 3, 13) # List based on https://registry.platformio.org/tools/platformio/framework-espidf/versions SUPPORTED_PLATFORMIO_ESP_IDF_5X = [ @@ -369,8 +400,8 @@ def _arduino_check_versions(value): def _esp_idf_check_versions(value): value = value.copy() lookups = { - "dev": (cv.Version(5, 1, 6), "https://github.com/espressif/esp-idf.git"), - "latest": (cv.Version(5, 1, 6), None), + "dev": (cv.Version(5, 3, 2), "https://github.com/espressif/esp-idf.git"), + "latest": (cv.Version(5, 3, 2), None), "recommended": (RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, None), } @@ -448,8 +479,8 @@ def _parse_platform_version(value): if ver.major >= 50: # a pioarduino version if "-" in value: # maybe a release candidate?...definitely not our default, just use it as-is... - return f"https://github.com/pioarduino/platform-espressif32.git#{value}" - return f"https://github.com/pioarduino/platform-espressif32.git#{ver.major}.{ver.minor:02d}.{ver.patch:02d}" + return f"https://github.com/pioarduino/platform-espressif32/releases/download/{value}/platform-espressif32.zip" + return f"https://github.com/pioarduino/platform-espressif32/releases/download/{ver.major}.{ver.minor:02d}.{ver.patch:02d}/platform-espressif32.zip" # if platform version is a valid version constraint, prefix the default package cv.platformio_version_constraint(value) return f"platformio/espressif32@{value}" @@ -539,6 +570,10 @@ ARDUINO_FRAMEWORK_SCHEMA = cv.All( ) CONF_SDKCONFIG_OPTIONS = "sdkconfig_options" +CONF_ENABLE_LWIP_DHCP_SERVER = "enable_lwip_dhcp_server" +CONF_ENABLE_LWIP_MDNS_QUERIES = "enable_lwip_mdns_queries" +CONF_ENABLE_LWIP_BRIDGE_INTERFACE = "enable_lwip_bridge_interface" + ESP_IDF_FRAMEWORK_SCHEMA = cv.All( cv.Schema( { @@ -551,11 +586,30 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All( }, cv.Optional(CONF_ADVANCED, default={}): cv.Schema( { + cv.Optional(CONF_ASSERTION_LEVEL): cv.one_of( + *ASSERTION_LEVELS, upper=True + ), + cv.Optional(CONF_COMPILER_OPTIMIZATION, default="SIZE"): cv.one_of( + *COMPILER_OPTIMIZATIONS, upper=True + ), + cv.Optional(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): cv.boolean, + cv.Optional(CONF_ENABLE_LWIP_ASSERT, default=True): cv.boolean, cv.Optional( CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False ): cv.boolean, cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC): cv.boolean, - cv.Optional(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): cv.boolean, + # DHCP server is needed for WiFi AP mode. When WiFi component is used, + # it will handle disabling DHCP server when AP is not configured. + # Default to false (disabled) when WiFi is not used. + cv.OnlyWithout( + CONF_ENABLE_LWIP_DHCP_SERVER, "wifi", default=False + ): cv.boolean, + cv.Optional( + CONF_ENABLE_LWIP_MDNS_QUERIES, default=False + ): cv.boolean, + cv.Optional( + CONF_ENABLE_LWIP_BRIDGE_INTERFACE, default=False + ): cv.boolean, } ), cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list( @@ -576,6 +630,21 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All( ) +def _set_default_framework(config): + if CONF_FRAMEWORK not in config: + config = config.copy() + + variant = config[CONF_VARIANT] + if variant in ARDUINO_ALLOWED_VARIANTS: + config[CONF_FRAMEWORK] = ARDUINO_FRAMEWORK_SCHEMA({}) + config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ARDUINO + else: + config[CONF_FRAMEWORK] = ESP_IDF_FRAMEWORK_SCHEMA({}) + config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ESP_IDF + + return config + + FRAMEWORK_ESP_IDF = "esp-idf" FRAMEWORK_ARDUINO = "arduino" FRAMEWORK_SCHEMA = cv.typed_schema( @@ -585,7 +654,6 @@ FRAMEWORK_SCHEMA = cv.typed_schema( }, lower=True, space="-", - default_type=FRAMEWORK_ARDUINO, ) @@ -612,10 +680,11 @@ CONFIG_SCHEMA = cv.All( ), cv.Optional(CONF_PARTITIONS): cv.file_, cv.Optional(CONF_VARIANT): cv.one_of(*VARIANTS, upper=True), - cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA, + cv.Optional(CONF_FRAMEWORK): FRAMEWORK_SCHEMA, } ), _detect_variant, + _set_default_framework, set_core_data, ) @@ -638,7 +707,7 @@ async def to_code(config): conf = config[CONF_FRAMEWORK] cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION]) - if CONF_ADVANCED in conf and conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]: + if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]: cg.add_define("USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC") add_extra_script( @@ -669,8 +738,6 @@ async def to_code(config): add_idf_sdkconfig_option( "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME", "partitions.csv" ) - add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False) - add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_SIZE", True) # Increase freertos tick speed from 100Hz to 1kHz so that delay() resolution is 1ms add_idf_sdkconfig_option("CONFIG_FREERTOS_HZ", 1000) @@ -684,16 +751,41 @@ async def to_code(config): # Set default CPU frequency add_idf_sdkconfig_option(f"CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_{freq}", True) + # Apply LWIP optimization settings + advanced = conf[CONF_ADVANCED] + # DHCP server: only disable if explicitly set to false + # WiFi component handles its own optimization when AP mode is not used + if ( + CONF_ENABLE_LWIP_DHCP_SERVER in advanced + and not advanced[CONF_ENABLE_LWIP_DHCP_SERVER] + ): + add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False) + if not advanced.get(CONF_ENABLE_LWIP_MDNS_QUERIES, False): + add_idf_sdkconfig_option("CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES", False) + if not advanced.get(CONF_ENABLE_LWIP_BRIDGE_INTERFACE, False): + add_idf_sdkconfig_option("CONFIG_LWIP_BRIDGEIF_MAX_PORTS", 0) + cg.add_platformio_option("board_build.partitions", "partitions.csv") if CONF_PARTITIONS in config: add_extra_build_file( "partitions.csv", CORE.relative_config_path(config[CONF_PARTITIONS]) ) - for name, value in conf[CONF_SDKCONFIG_OPTIONS].items(): - add_idf_sdkconfig_option(name, RawSdkconfigValue(value)) + if assertion_level := advanced.get(CONF_ASSERTION_LEVEL): + for key, flag in ASSERTION_LEVELS.items(): + add_idf_sdkconfig_option(flag, assertion_level == key) - if conf[CONF_ADVANCED].get(CONF_IGNORE_EFUSE_MAC_CRC): + add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False) + compiler_optimization = advanced.get(CONF_COMPILER_OPTIMIZATION) + for key, flag in COMPILER_OPTIMIZATIONS.items(): + add_idf_sdkconfig_option(flag, compiler_optimization == key) + + add_idf_sdkconfig_option( + "CONFIG_LWIP_ESP_LWIP_ASSERT", + conf[CONF_ADVANCED][CONF_ENABLE_LWIP_ASSERT], + ) + + if advanced.get(CONF_IGNORE_EFUSE_MAC_CRC): add_idf_sdkconfig_option("CONFIG_ESP_MAC_IGNORE_MAC_CRC_ERROR", True) if (framework_ver.major, framework_ver.minor) >= (4, 4): add_idf_sdkconfig_option( @@ -703,7 +795,7 @@ async def to_code(config): add_idf_sdkconfig_option( "CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE", False ) - if conf[CONF_ADVANCED].get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): + if advanced.get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): _LOGGER.warning( "Using experimental features in ESP-IDF may result in unexpected failures." ) @@ -716,6 +808,9 @@ async def to_code(config): ), ) + for name, value in conf[CONF_SDKCONFIG_OPTIONS].items(): + add_idf_sdkconfig_option(name, RawSdkconfigValue(value)) + for component in conf[CONF_COMPONENTS]: source = component[CONF_SOURCE] if source[CONF_TYPE] == TYPE_GIT: diff --git a/esphome/components/esp32/boards.py b/esphome/components/esp32/boards.py index 81400eb9c3..68fee48830 100644 --- a/esphome/components/esp32/boards.py +++ b/esphome/components/esp32/boards.py @@ -4,6 +4,7 @@ from .const import ( VARIANT_ESP32C3, VARIANT_ESP32C6, VARIANT_ESP32H2, + VARIANT_ESP32P4, VARIANT_ESP32S2, VARIANT_ESP32S3, ) @@ -1338,17 +1339,7 @@ ESP32_BOARD_PINS = { } """ -BOARDS generated with: - -git clone https://github.com/platformio/platform-espressif32 -for x in platform-espressif32/boards/*.json; do - mcu=$(jq -r .build.mcu <"$x"); - name=$(jq -r .name <"$x"); - fname=$(basename "$x") - board="${fname%.*}" - variant=$(echo "$mcu" | tr '[:lower:]' '[:upper:]') - echo " \"$board\": {\"name\": \"$name\", \"variant\": VARIANT_${variant},}," -done | sort +BOARDS generated with script/generate-esp32-boards.py """ BOARDS = { @@ -1360,6 +1351,10 @@ BOARDS = { "name": "Adafruit pyCamera S3", "variant": VARIANT_ESP32S3, }, + "adafruit_feather_esp32_v2": { + "name": "Adafruit Feather ESP32 V2", + "variant": VARIANT_ESP32, + }, "adafruit_feather_esp32c6": { "name": "Adafruit Feather ESP32-C6", "variant": VARIANT_ESP32C6, @@ -1392,10 +1387,6 @@ BOARDS = { "name": "Adafruit Feather ESP32-S3 TFT", "variant": VARIANT_ESP32S3, }, - "adafruit_feather_esp32_v2": { - "name": "Adafruit Feather ESP32 V2", - "variant": VARIANT_ESP32, - }, "adafruit_funhouse_esp32s2": { "name": "Adafruit FunHouse", "variant": VARIANT_ESP32S2, @@ -1420,14 +1411,14 @@ BOARDS = { "name": "Adafruit Metro ESP32-S3", "variant": VARIANT_ESP32S3, }, - "adafruit_qtpy_esp32c3": { - "name": "Adafruit QT Py ESP32-C3", - "variant": VARIANT_ESP32C3, - }, "adafruit_qtpy_esp32": { "name": "Adafruit QT Py ESP32", "variant": VARIANT_ESP32, }, + "adafruit_qtpy_esp32c3": { + "name": "Adafruit QT Py ESP32-C3", + "variant": VARIANT_ESP32C3, + }, "adafruit_qtpy_esp32s2": { "name": "Adafruit QT Py ESP32-S2", "variant": VARIANT_ESP32S2, @@ -1476,14 +1467,14 @@ BOARDS = { "name": "Smart Bee Data Logger", "variant": VARIANT_ESP32S3, }, - "bee_motion_mini": { - "name": "Smart Bee Motion Mini", - "variant": VARIANT_ESP32C3, - }, "bee_motion": { "name": "Smart Bee Motion", "variant": VARIANT_ESP32S2, }, + "bee_motion_mini": { + "name": "Smart Bee Motion Mini", + "variant": VARIANT_ESP32C3, + }, "bee_motion_s3": { "name": "Smart Bee Motion S3", "variant": VARIANT_ESP32S3, @@ -1516,6 +1507,10 @@ BOARDS = { "name": "D-duino-32", "variant": VARIANT_ESP32, }, + "deneyapkart": { + "name": "Deneyap Kart", + "variant": VARIANT_ESP32, + }, "deneyapkart1A": { "name": "Deneyap Kart 1A", "variant": VARIANT_ESP32, @@ -1528,10 +1523,6 @@ BOARDS = { "name": "Deneyap Kart G", "variant": VARIANT_ESP32C3, }, - "deneyapkart": { - "name": "Deneyap Kart", - "variant": VARIANT_ESP32, - }, "deneyapmini": { "name": "Deneyap Mini", "variant": VARIANT_ESP32S2, @@ -1572,8 +1563,8 @@ BOARDS = { "name": "Seeed Studio Edgebox-ESP-100", "variant": VARIANT_ESP32S3, }, - "esp320": { - "name": "Electronic SweetPeas ESP320", + "esp-wrover-kit": { + "name": "Espressif ESP-WROVER-KIT", "variant": VARIANT_ESP32, }, "esp32-c2-devkitm-1": { @@ -1600,26 +1591,10 @@ BOARDS = { "name": "Espressif ESP32-C6-DevKitM-1", "variant": VARIANT_ESP32C6, }, - "esp32cam": { - "name": "AI Thinker ESP32-CAM", - "variant": VARIANT_ESP32, - }, "esp32-devkitlipo": { "name": "OLIMEX ESP32-DevKit-LiPo", "variant": VARIANT_ESP32, }, - "esp32dev": { - "name": "Espressif ESP32 Dev Module", - "variant": VARIANT_ESP32, - }, - "esp32doit-devkit-v1": { - "name": "DOIT ESP32 DEVKIT V1", - "variant": VARIANT_ESP32, - }, - "esp32doit-espduino": { - "name": "DOIT ESPduino32", - "variant": VARIANT_ESP32, - }, "esp32-evb": { "name": "OLIMEX ESP32-EVB", "variant": VARIANT_ESP32, @@ -1632,18 +1607,26 @@ BOARDS = { "name": "Espressif ESP32-H2-DevKit", "variant": VARIANT_ESP32H2, }, + "esp32-p4": { + "name": "Espressif ESP32-P4 generic", + "variant": VARIANT_ESP32P4, + }, + "esp32-p4-evboard": { + "name": "Espressif ESP32-P4 Function EV Board", + "variant": VARIANT_ESP32P4, + }, "esp32-pico-devkitm-2": { "name": "Espressif ESP32-PICO-DevKitM-2", "variant": VARIANT_ESP32, }, - "esp32-poe-iso": { - "name": "OLIMEX ESP32-PoE-ISO", - "variant": VARIANT_ESP32, - }, "esp32-poe": { "name": "OLIMEX ESP32-PoE", "variant": VARIANT_ESP32, }, + "esp32-poe-iso": { + "name": "OLIMEX ESP32-PoE-ISO", + "variant": VARIANT_ESP32, + }, "esp32-pro": { "name": "OLIMEX ESP32-PRO", "variant": VARIANT_ESP32, @@ -1660,14 +1643,6 @@ BOARDS = { "name": "Espressif ESP32-S2-Saola-1", "variant": VARIANT_ESP32S2, }, - "esp32s3box": { - "name": "Espressif ESP32-S3-Box", - "variant": VARIANT_ESP32S3, - }, - "esp32s3camlcd": { - "name": "ESP32S3 CAM LCD", - "variant": VARIANT_ESP32S3, - }, "esp32-s3-devkitc-1": { "name": "Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)", "variant": VARIANT_ESP32S3, @@ -1676,18 +1651,50 @@ BOARDS = { "name": "Espressif ESP32-S3-DevKitM-1", "variant": VARIANT_ESP32S3, }, + "esp32-solo1": { + "name": "Espressif Generic ESP32-solo1 4M Flash", + "variant": VARIANT_ESP32, + }, + "esp320": { + "name": "Electronic SweetPeas ESP320", + "variant": VARIANT_ESP32, + }, + "esp32cam": { + "name": "AI Thinker ESP32-CAM", + "variant": VARIANT_ESP32, + }, + "esp32dev": { + "name": "Espressif ESP32 Dev Module", + "variant": VARIANT_ESP32, + }, + "esp32doit-devkit-v1": { + "name": "DOIT ESP32 DEVKIT V1", + "variant": VARIANT_ESP32, + }, + "esp32doit-espduino": { + "name": "DOIT ESPduino32", + "variant": VARIANT_ESP32, + }, + "esp32s3_120_16_8-qio_opi": { + "name": "ESP32-S3 16MB QIO, 8MB OPI PSRAM", + "variant": VARIANT_ESP32S3, + }, "esp32s3_powerfeather": { "name": "ESP32-S3 PowerFeather", "variant": VARIANT_ESP32S3, }, + "esp32s3box": { + "name": "Espressif ESP32-S3-Box", + "variant": VARIANT_ESP32S3, + }, + "esp32s3camlcd": { + "name": "ESP32S3 CAM LCD", + "variant": VARIANT_ESP32S3, + }, "esp32s3usbotg": { "name": "Espressif ESP32-S3-USB-OTG", "variant": VARIANT_ESP32S3, }, - "esp32-solo1": { - "name": "Espressif Generic ESP32-solo1 4M Flash", - "variant": VARIANT_ESP32, - }, "esp32thing": { "name": "SparkFun ESP32 Thing", "variant": VARIANT_ESP32, @@ -1712,10 +1719,6 @@ BOARDS = { "name": "ESPino32", "variant": VARIANT_ESP32, }, - "esp-wrover-kit": { - "name": "Espressif ESP-WROVER-KIT", - "variant": VARIANT_ESP32, - }, "etboard": { "name": "ETBoard", "variant": VARIANT_ESP32, @@ -1744,6 +1747,14 @@ BOARDS = { "name": "Franzininho WiFi MSC", "variant": VARIANT_ESP32S2, }, + "freenove_esp32_s3_wroom": { + "name": "Freenove ESP32-S3 WROOM N8R8 (8MB Flash / 8MB PSRAM)", + "variant": VARIANT_ESP32S3, + }, + "freenove_esp32_wrover": { + "name": "Freenove ESP32-Wrover", + "variant": VARIANT_ESP32, + }, "frogboard": { "name": "Frog Board ESP32", "variant": VARIANT_ESP32, @@ -1760,6 +1771,10 @@ BOARDS = { "name": "Heltec WiFi Kit 32 (V3)", "variant": VARIANT_ESP32S3, }, + "heltec_wifi_kit_32_v2": { + "name": "Heltec WiFi Kit 32 (V2)", + "variant": VARIANT_ESP32, + }, "heltec_wifi_lora_32": { "name": "Heltec WiFi LoRa 32", "variant": VARIANT_ESP32, @@ -1772,14 +1787,14 @@ BOARDS = { "name": "Heltec WiFi LoRa 32 (V3)", "variant": VARIANT_ESP32S3, }, - "heltec_wireless_stick_lite": { - "name": "Heltec Wireless Stick Lite", - "variant": VARIANT_ESP32, - }, "heltec_wireless_stick": { "name": "Heltec Wireless Stick", "variant": VARIANT_ESP32, }, + "heltec_wireless_stick_lite": { + "name": "Heltec Wireless Stick Lite", + "variant": VARIANT_ESP32, + }, "honeylemon": { "name": "HONEYLemon", "variant": VARIANT_ESP32, @@ -1792,6 +1807,14 @@ BOARDS = { "name": "Hornbill ESP32 Minima", "variant": VARIANT_ESP32, }, + "huidu_hd_wf2": { + "name": "Huidu HD-WF2", + "variant": VARIANT_ESP32S3, + }, + "huidu_hd_wf4": { + "name": "Huidu HD-WF4", + "variant": VARIANT_ESP32S3, + }, "imbrios-logsens-v1p1": { "name": "Imbrios LogSens V1P1", "variant": VARIANT_ESP32, @@ -1824,6 +1847,10 @@ BOARDS = { "name": "ArtronShop IOXESP32PS", "variant": VARIANT_ESP32, }, + "jczn_2432s028r": { + "name": "ESP32-2432S028R CYD", + "variant": VARIANT_ESP32, + }, "kb32-ft": { "name": "MakerAsia KB32-FT", "variant": VARIANT_ESP32, @@ -1848,6 +1875,10 @@ BOARDS = { "name": "LilyGo T-Display-S3", "variant": VARIANT_ESP32S3, }, + "lilygo-t3-s3": { + "name": "LilyGo T3-S3", + "variant": VARIANT_ESP32S3, + }, "lionbit": { "name": "Lion:Bit Dev Board", "variant": VARIANT_ESP32, @@ -1856,14 +1887,14 @@ BOARDS = { "name": "Lion:Bit S3 STEM Dev Board", "variant": VARIANT_ESP32S3, }, - "lolin32_lite": { - "name": "WEMOS LOLIN32 Lite", - "variant": VARIANT_ESP32, - }, "lolin32": { "name": "WEMOS LOLIN32", "variant": VARIANT_ESP32, }, + "lolin32_lite": { + "name": "WEMOS LOLIN32 Lite", + "variant": VARIANT_ESP32, + }, "lolin_c3_mini": { "name": "WEMOS LOLIN C3 Mini", "variant": VARIANT_ESP32C3, @@ -1884,26 +1915,30 @@ BOARDS = { "name": "WEMOS LOLIN S2 PICO", "variant": VARIANT_ESP32S2, }, + "lolin_s3": { + "name": "WEMOS LOLIN S3", + "variant": VARIANT_ESP32S3, + }, "lolin_s3_mini": { "name": "WEMOS LOLIN S3 Mini", "variant": VARIANT_ESP32S3, }, - "lolin_s3": { - "name": "WEMOS LOLIN S3", + "lolin_s3_mini_pro": { + "name": "WEMOS LOLIN S3 Mini Pro", "variant": VARIANT_ESP32S3, }, "lolin_s3_pro": { "name": "WEMOS LOLIN S3 PRO", "variant": VARIANT_ESP32S3, }, - "lopy4": { - "name": "Pycom LoPy4", - "variant": VARIANT_ESP32, - }, "lopy": { "name": "Pycom LoPy", "variant": VARIANT_ESP32, }, + "lopy4": { + "name": "Pycom LoPy4", + "variant": VARIANT_ESP32, + }, "m5stack-atom": { "name": "M5Stack-ATOM", "variant": VARIANT_ESP32, @@ -1912,16 +1947,16 @@ BOARDS = { "name": "M5Stack AtomS3", "variant": VARIANT_ESP32S3, }, - "m5stack-core2": { - "name": "M5Stack Core2", + "m5stack-core-esp32": { + "name": "M5Stack Core ESP32", "variant": VARIANT_ESP32, }, "m5stack-core-esp32-16M": { "name": "M5Stack Core ESP32 16M", "variant": VARIANT_ESP32, }, - "m5stack-core-esp32": { - "name": "M5Stack Core ESP32", + "m5stack-core2": { + "name": "M5Stack Core2", "variant": VARIANT_ESP32, }, "m5stack-coreink": { @@ -1940,10 +1975,6 @@ BOARDS = { "name": "M5Stack GREY ESP32", "variant": VARIANT_ESP32, }, - "m5stack_paper": { - "name": "M5Stack Paper", - "variant": VARIANT_ESP32, - }, "m5stack-stamps3": { "name": "M5Stack StampS3", "variant": VARIANT_ESP32S3, @@ -1956,6 +1987,10 @@ BOARDS = { "name": "M5Stack Timer CAM", "variant": VARIANT_ESP32, }, + "m5stack_paper": { + "name": "M5Stack Paper", + "variant": VARIANT_ESP32, + }, "m5stamp-pico": { "name": "M5Stamp-Pico", "variant": VARIANT_ESP32, @@ -2024,14 +2059,14 @@ BOARDS = { "name": "Node32s", "variant": VARIANT_ESP32, }, - "nodemcu-32s2": { - "name": "Ai-Thinker NodeMCU-32S2 (ESP-12K)", - "variant": VARIANT_ESP32S2, - }, "nodemcu-32s": { "name": "NodeMCU-32S", "variant": VARIANT_ESP32, }, + "nodemcu-32s2": { + "name": "Ai-Thinker NodeMCU-32S2 (ESP-12K)", + "variant": VARIANT_ESP32S2, + }, "nscreen-32": { "name": "YeaCreate NSCREEN-32", "variant": VARIANT_ESP32, @@ -2080,10 +2115,22 @@ BOARDS = { "name": "RoboHeart Hercules", "variant": VARIANT_ESP32, }, + "rymcu-esp32-s3-devkitc-1": { + "name": "RYMCU ESP32-S3-DevKitC-1-N8R2 (8 MB QD, 2 MB PSRAM)", + "variant": VARIANT_ESP32S3, + }, + "s_odi_ultra": { + "name": "S.ODI Ultra v1", + "variant": VARIANT_ESP32, + }, "seeed_xiao_esp32c3": { "name": "Seeed Studio XIAO ESP32C3", "variant": VARIANT_ESP32C3, }, + "seeed_xiao_esp32c6": { + "name": "Seeed Studio XIAO ESP32C6", + "variant": VARIANT_ESP32C6, + }, "seeed_xiao_esp32s3": { "name": "Seeed Studio XIAO ESP32S3", "variant": VARIANT_ESP32S3, @@ -2100,34 +2147,38 @@ BOARDS = { "name": "SG-O AirMon", "variant": VARIANT_ESP32, }, - "s_odi_ultra": { - "name": "S.ODI Ultra v1", + "sparkfun_esp32_iot_redboard": { + "name": "SparkFun ESP32 IoT RedBoard", "variant": VARIANT_ESP32, }, "sparkfun_esp32c6_thing_plus": { "name": "Sparkfun ESP32-C6 Thing Plus", "variant": VARIANT_ESP32C6, }, - "sparkfun_esp32_iot_redboard": { - "name": "SparkFun ESP32 IoT RedBoard", - "variant": VARIANT_ESP32, - }, "sparkfun_esp32micromod": { "name": "SparkFun ESP32 MicroMod", "variant": VARIANT_ESP32, }, - "sparkfun_esp32s2_thing_plus_c": { - "name": "SparkFun ESP32 Thing Plus C", - "variant": VARIANT_ESP32, - }, "sparkfun_esp32s2_thing_plus": { "name": "SparkFun ESP32-S2 Thing Plus", "variant": VARIANT_ESP32S2, }, + "sparkfun_esp32s2_thing_plus_c": { + "name": "SparkFun ESP32 Thing Plus C", + "variant": VARIANT_ESP32, + }, + "sparkfun_esp32s3_thing_plus": { + "name": "SPARKFUN_ESP32S3_THING_PLUS", + "variant": VARIANT_ESP32S3, + }, "sparkfun_lora_gateway_1-channel": { "name": "SparkFun LoRa Gateway 1-Channel", "variant": VARIANT_ESP32, }, + "sparkfun_qwiic_pocket_esp32c6": { + "name": "SparkFun ESP32-C6 Qwiic Pocket", + "variant": VARIANT_ESP32C6, + }, "tamc_termod_s3": { "name": "TAMC Termod S3", "variant": VARIANT_ESP32S3, @@ -2136,6 +2187,10 @@ BOARDS = { "name": "Unexpected Maker TinyPICO", "variant": VARIANT_ESP32, }, + "trueverit-iot-driver": { + "name": "Trueverit ESP32 Universal IoT Driver", + "variant": VARIANT_ESP32, + }, "trueverit-iot-driver-mk2": { "name": "Trueverit ESP32 Universal IoT Driver MK II", "variant": VARIANT_ESP32, @@ -2144,32 +2199,16 @@ BOARDS = { "name": "Trueverit ESP32 Universal IoT Driver MK III", "variant": VARIANT_ESP32, }, - "trueverit-iot-driver": { - "name": "Trueverit ESP32 Universal IoT Driver", - "variant": VARIANT_ESP32, - }, "ttgo-lora32-v1": { "name": "TTGO LoRa32-OLED V1", "variant": VARIANT_ESP32, }, - "ttgo-lora32-v21": { - "name": "TTGO LoRa32-OLED v2.1.6", - "variant": VARIANT_ESP32, - }, "ttgo-lora32-v2": { "name": "TTGO LoRa32-OLED V2", "variant": VARIANT_ESP32, }, - "ttgo-t1": { - "name": "TTGO T1", - "variant": VARIANT_ESP32, - }, - "ttgo-t7-v13-mini32": { - "name": "TTGO T7 V1.3 Mini32", - "variant": VARIANT_ESP32, - }, - "ttgo-t7-v14-mini32": { - "name": "TTGO T7 V1.4 Mini32", + "ttgo-lora32-v21": { + "name": "TTGO LoRa32-OLED v2.1.6", "variant": VARIANT_ESP32, }, "ttgo-t-beam": { @@ -2184,6 +2223,18 @@ BOARDS = { "name": "TTGO T-Watch", "variant": VARIANT_ESP32, }, + "ttgo-t1": { + "name": "TTGO T1", + "variant": VARIANT_ESP32, + }, + "ttgo-t7-v13-mini32": { + "name": "TTGO T7 V1.3 Mini32", + "variant": VARIANT_ESP32, + }, + "ttgo-t7-v14-mini32": { + "name": "TTGO T7 V1.4 Mini32", + "variant": VARIANT_ESP32, + }, "turta_iot_node": { "name": "Turta IoT Node", "variant": VARIANT_ESP32, @@ -2256,9 +2307,17 @@ BOARDS = { "name": "SQFMI Watchy v2.0", "variant": VARIANT_ESP32, }, - "wemosbat": { - "name": "WeMos WiFi and Bluetooth Battery", - "variant": VARIANT_ESP32, + "waveshare_esp32_s3_zero": { + "name": "Waveshare ESP32-S3-Zero", + "variant": VARIANT_ESP32S3, + }, + "waveshare_esp32s3_touch_lcd_128": { + "name": "Waveshare ESP32-S3-Touch-LCD-1.28 (16 MB QD, 2MB PSRAM)", + "variant": VARIANT_ESP32S3, + }, + "weactstudio_esp32c3coreboard": { + "name": "WeAct Studio ESP32C3CoreBoard", + "variant": VARIANT_ESP32C3, }, "wemos_d1_mini32": { "name": "WEMOS D1 MINI ESP32", @@ -2268,6 +2327,10 @@ BOARDS = { "name": "WEMOS D1 R32", "variant": VARIANT_ESP32, }, + "wemosbat": { + "name": "WeMos WiFi and Bluetooth Battery", + "variant": VARIANT_ESP32, + }, "wesp32": { "name": "Silicognition wESP32", "variant": VARIANT_ESP32, @@ -2276,14 +2339,14 @@ BOARDS = { "name": "Widora AIR", "variant": VARIANT_ESP32, }, - "wifiduino32c3": { - "name": "Blinker WiFiduinoV2 (ESP32-C3)", - "variant": VARIANT_ESP32C3, - }, "wifiduino32": { "name": "Blinker WiFiduino32", "variant": VARIANT_ESP32, }, + "wifiduino32c3": { + "name": "Blinker WiFiduinoV2 (ESP32-C3)", + "variant": VARIANT_ESP32C3, + }, "wifiduino32s3": { "name": "Blinker WiFiduino32S3", "variant": VARIANT_ESP32S3, @@ -2292,12 +2355,32 @@ BOARDS = { "name": "Pycom WiPy3", "variant": VARIANT_ESP32, }, + "ws_esp32_s3_matrix": { + "name": "Waveshare ESP32-S3-Matrix", + "variant": VARIANT_ESP32S3, + }, "wt32-eth01": { "name": "Wireless-Tag WT32-ETH01 Ethernet Module", "variant": VARIANT_ESP32, }, + "wt32-sc01-plus": { + "name": "wt32-sc01-plus", + "variant": VARIANT_ESP32S3, + }, "xinabox_cw02": { "name": "XinaBox CW02", "variant": VARIANT_ESP32, }, + "yb_esp32s3_amp_v2": { + "name": "YelloByte YB-ESP32-S3-AMP (Rev.2)", + "variant": VARIANT_ESP32S3, + }, + "yb_esp32s3_amp_v3": { + "name": "YelloByte YB-ESP32-S3-AMP (Rev.3)", + "variant": VARIANT_ESP32S3, + }, + "yb_esp32s3_eth": { + "name": "YelloByte YB-ESP32-S3-ETH", + "variant": VARIANT_ESP32S3, + }, } diff --git a/esphome/components/esp32/const.py b/esphome/components/esp32/const.py index a86713e857..9bef18847f 100644 --- a/esphome/components/esp32/const.py +++ b/esphome/components/esp32/const.py @@ -17,16 +17,20 @@ VARIANT_ESP32S2 = "ESP32S2" VARIANT_ESP32S3 = "ESP32S3" VARIANT_ESP32C2 = "ESP32C2" VARIANT_ESP32C3 = "ESP32C3" +VARIANT_ESP32C5 = "ESP32C5" VARIANT_ESP32C6 = "ESP32C6" VARIANT_ESP32H2 = "ESP32H2" +VARIANT_ESP32P4 = "ESP32P4" VARIANTS = [ VARIANT_ESP32, VARIANT_ESP32S2, VARIANT_ESP32S3, VARIANT_ESP32C2, VARIANT_ESP32C3, + VARIANT_ESP32C5, VARIANT_ESP32C6, VARIANT_ESP32H2, + VARIANT_ESP32P4, ] VARIANT_FRIENDLY = { @@ -35,8 +39,10 @@ VARIANT_FRIENDLY = { VARIANT_ESP32S3: "ESP32-S3", VARIANT_ESP32C2: "ESP32-C2", VARIANT_ESP32C3: "ESP32-C3", + VARIANT_ESP32C5: "ESP32-C5", VARIANT_ESP32C6: "ESP32-C6", VARIANT_ESP32H2: "ESP32-H2", + VARIANT_ESP32P4: "ESP32-P4", } esp32_ns = cg.esphome_ns.namespace("esp32") diff --git a/esphome/components/esp32/gpio.py b/esphome/components/esp32/gpio.py index 2bb10ce6ec..c35e5c2215 100644 --- a/esphome/components/esp32/gpio.py +++ b/esphome/components/esp32/gpio.py @@ -1,6 +1,7 @@ +from collections.abc import Callable from dataclasses import dataclass import logging -from typing import Any, Callable +from typing import Any from esphome import pins import esphome.codegen as cg @@ -26,8 +27,10 @@ from .const import ( VARIANT_ESP32, VARIANT_ESP32C2, VARIANT_ESP32C3, + VARIANT_ESP32C5, VARIANT_ESP32C6, VARIANT_ESP32H2, + VARIANT_ESP32P4, VARIANT_ESP32S2, VARIANT_ESP32S3, esp32_ns, @@ -35,8 +38,10 @@ from .const import ( from .gpio_esp32 import esp32_validate_gpio_pin, esp32_validate_supports from .gpio_esp32_c2 import esp32_c2_validate_gpio_pin, esp32_c2_validate_supports from .gpio_esp32_c3 import esp32_c3_validate_gpio_pin, esp32_c3_validate_supports +from .gpio_esp32_c5 import esp32_c5_validate_gpio_pin, esp32_c5_validate_supports from .gpio_esp32_c6 import esp32_c6_validate_gpio_pin, esp32_c6_validate_supports from .gpio_esp32_h2 import esp32_h2_validate_gpio_pin, esp32_h2_validate_supports +from .gpio_esp32_p4 import esp32_p4_validate_gpio_pin, esp32_p4_validate_supports from .gpio_esp32_s2 import esp32_s2_validate_gpio_pin, esp32_s2_validate_supports from .gpio_esp32_s3 import esp32_s3_validate_gpio_pin, esp32_s3_validate_supports @@ -97,6 +102,10 @@ _esp32_validations = { pin_validation=esp32_c3_validate_gpio_pin, usage_validation=esp32_c3_validate_supports, ), + VARIANT_ESP32C5: ESP32ValidationFunctions( + pin_validation=esp32_c5_validate_gpio_pin, + usage_validation=esp32_c5_validate_supports, + ), VARIANT_ESP32C6: ESP32ValidationFunctions( pin_validation=esp32_c6_validate_gpio_pin, usage_validation=esp32_c6_validate_supports, @@ -105,6 +114,10 @@ _esp32_validations = { pin_validation=esp32_h2_validate_gpio_pin, usage_validation=esp32_h2_validate_supports, ), + VARIANT_ESP32P4: ESP32ValidationFunctions( + pin_validation=esp32_p4_validate_gpio_pin, + usage_validation=esp32_p4_validate_supports, + ), VARIANT_ESP32S2: ESP32ValidationFunctions( pin_validation=esp32_s2_validate_gpio_pin, usage_validation=esp32_s2_validate_supports, diff --git a/esphome/components/esp32/gpio_esp32_c5.py b/esphome/components/esp32/gpio_esp32_c5.py new file mode 100644 index 0000000000..ada426771c --- /dev/null +++ b/esphome/components/esp32/gpio_esp32_c5.py @@ -0,0 +1,45 @@ +import logging + +import esphome.config_validation as cv +from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER +from esphome.pins import check_strapping_pin + +_ESP32C5_SPI_PSRAM_PINS = { + 16: "SPICS0", + 17: "SPIQ", + 18: "SPIWP", + 19: "VDD_SPI", + 20: "SPIHD", + 21: "SPICLK", + 22: "SPID", +} + +_ESP32C5_STRAPPING_PINS = {2, 7, 27, 28} + +_LOGGER = logging.getLogger(__name__) + + +def esp32_c5_validate_gpio_pin(value): + if value < 0 or value > 28: + raise cv.Invalid(f"Invalid pin number: {value} (must be 0-28)") + if value in _ESP32C5_SPI_PSRAM_PINS: + raise cv.Invalid( + f"This pin cannot be used on ESP32-C5s and is already used by the SPI/PSRAM interface (function: {_ESP32C5_SPI_PSRAM_PINS[value]})" + ) + + return value + + +def esp32_c5_validate_supports(value): + num = value[CONF_NUMBER] + mode = value[CONF_MODE] + is_input = mode[CONF_INPUT] + + if num < 0 or num > 28: + raise cv.Invalid(f"Invalid pin number: {num} (must be 0-28)") + if is_input: + # All ESP32 pins support input mode + pass + + check_strapping_pin(value, _ESP32C5_STRAPPING_PINS, _LOGGER) + return value diff --git a/esphome/components/esp32/gpio_esp32_p4.py b/esphome/components/esp32/gpio_esp32_p4.py new file mode 100644 index 0000000000..650d06e108 --- /dev/null +++ b/esphome/components/esp32/gpio_esp32_p4.py @@ -0,0 +1,43 @@ +import logging + +import esphome.config_validation as cv +from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER + +_ESP32P4_USB_JTAG_PINS = {24, 25} + +_ESP32P4_STRAPPING_PINS = {34, 35, 36, 37, 38} + +_LOGGER = logging.getLogger(__name__) + + +def esp32_p4_validate_gpio_pin(value): + if value < 0 or value > 54: + raise cv.Invalid(f"Invalid pin number: {value} (must be 0-54)") + if value in _ESP32P4_STRAPPING_PINS: + _LOGGER.warning( + "GPIO%d is a Strapping PIN and should be avoided.\n" + "Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n" + "See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins", + value, + ) + if value in _ESP32P4_USB_JTAG_PINS: + _LOGGER.warning( + "GPIO%d is reserved for the USB-Serial-JTAG interface.\n" + "To use this pin as GPIO, USB-Serial-JTAG will be disabled.", + value, + ) + + return value + + +def esp32_p4_validate_supports(value): + num = value[CONF_NUMBER] + mode = value[CONF_MODE] + is_input = mode[CONF_INPUT] + + if num < 0 or num > 54: + raise cv.Invalid(f"Invalid pin number: {value} (must be 0-54)") + if is_input: + # All ESP32 pins support input mode + pass + return value diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index f90b8a4603..e53cdd90d3 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -1,8 +1,8 @@ #ifdef USE_ESP32 -#include "esphome/core/preferences.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "esphome/core/preferences.h" #include #include #include @@ -84,7 +84,7 @@ class ESP32Preferences : public ESPPreferences { if (err == 0) return; - ESP_LOGW(TAG, "nvs_open failed: %s - erasing NVS...", esp_err_to_name(err)); + ESP_LOGW(TAG, "nvs_open failed: %s - erasing NVS", esp_err_to_name(err)); nvs_flash_deinit(); nvs_flash_erase(); nvs_flash_init(); @@ -111,7 +111,7 @@ class ESP32Preferences : public ESPPreferences { if (s_pending_save.empty()) return true; - ESP_LOGD(TAG, "Saving %d preferences to flash...", s_pending_save.size()); + ESP_LOGV(TAG, "Saving %d items...", s_pending_save.size()); // goal try write all pending saves even if one fails int cached = 0, written = 0, failed = 0; esp_err_t last_err = ESP_OK; @@ -139,10 +139,10 @@ class ESP32Preferences : public ESPPreferences { } s_pending_save.erase(s_pending_save.begin() + i); } - ESP_LOGD(TAG, "Saving %d preferences to flash: %d cached, %d written, %d failed", cached + written + failed, cached, - written, failed); + ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written, + failed); if (failed > 0) { - ESP_LOGE(TAG, "Error saving %d preferences to flash. Last error=%s for key=%s", failed, esp_err_to_name(last_err), + ESP_LOGE(TAG, "Writing %d items failed. Last error=%s for key=%s", failed, esp_err_to_name(last_err), last_key.c_str()); } @@ -173,7 +173,7 @@ class ESP32Preferences : public ESPPreferences { } bool reset() override { - ESP_LOGD(TAG, "Cleaning up preferences in flash..."); + ESP_LOGD(TAG, "Erasing storage"); s_pending_save.clear(); nvs_flash_deinit(); diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index 37b4900a03..93bb643596 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -1,3 +1,4 @@ +from enum import Enum import re from esphome import automation @@ -12,9 +13,110 @@ import esphome.final_validate as fv DEPENDENCIES = ["esp32"] CODEOWNERS = ["@jesserockz", "@Rapsssito"] + +class BTLoggers(Enum): + """Bluetooth logger categories available in ESP-IDF. + + Each logger controls debug output for a specific Bluetooth subsystem. + The value is the ESP-IDF sdkconfig option name for controlling the log level. + """ + + # Core Stack Layers + HCI = "CONFIG_BT_LOG_HCI_TRACE_LEVEL" + """Host Controller Interface - Low-level interface between host and controller""" + + BTM = "CONFIG_BT_LOG_BTM_TRACE_LEVEL" + """Bluetooth Manager - Core device control, connections, and security""" + + L2CAP = "CONFIG_BT_LOG_L2CAP_TRACE_LEVEL" + """Logical Link Control and Adaptation Protocol - Connection multiplexing""" + + RFCOMM = "CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL" + """Serial port emulation over Bluetooth (Classic only)""" + + SDP = "CONFIG_BT_LOG_SDP_TRACE_LEVEL" + """Service Discovery Protocol - Service discovery (Classic only)""" + + GAP = "CONFIG_BT_LOG_GAP_TRACE_LEVEL" + """Generic Access Profile - Device discovery and connections""" + + # Network Protocols + BNEP = "CONFIG_BT_LOG_BNEP_TRACE_LEVEL" + """Bluetooth Network Encapsulation Protocol - IP over Bluetooth""" + + PAN = "CONFIG_BT_LOG_PAN_TRACE_LEVEL" + """Personal Area Networking - Ethernet over Bluetooth""" + + # Audio/Video Profiles (Classic Bluetooth) + A2D = "CONFIG_BT_LOG_A2D_TRACE_LEVEL" + """Advanced Audio Distribution - A2DP audio streaming""" + + AVDT = "CONFIG_BT_LOG_AVDT_TRACE_LEVEL" + """Audio/Video Distribution Transport - A2DP transport protocol""" + + AVCT = "CONFIG_BT_LOG_AVCT_TRACE_LEVEL" + """Audio/Video Control Transport - AVRCP transport protocol""" + + AVRC = "CONFIG_BT_LOG_AVRC_TRACE_LEVEL" + """Audio/Video Remote Control - Media playback control""" + + # Security + SMP = "CONFIG_BT_LOG_SMP_TRACE_LEVEL" + """Security Manager Protocol - BLE pairing and encryption""" + + # Application Layer + BTIF = "CONFIG_BT_LOG_BTIF_TRACE_LEVEL" + """Bluetooth Interface - Application interface layer""" + + BTC = "CONFIG_BT_LOG_BTC_TRACE_LEVEL" + """Bluetooth Common - Task handling and coordination""" + + # BLE Specific + BLE_SCAN = "CONFIG_BT_LOG_BLE_SCAN_TRACE_LEVEL" + """BLE scanning operations""" + + GATT = "CONFIG_BT_LOG_GATT_TRACE_LEVEL" + """Generic Attribute Profile - BLE data exchange protocol""" + + # Other Profiles + MCA = "CONFIG_BT_LOG_MCA_TRACE_LEVEL" + """Multi-Channel Adaptation - Health device profile""" + + HID = "CONFIG_BT_LOG_HID_TRACE_LEVEL" + """Human Interface Device - Keyboards, mice, controllers""" + + APPL = "CONFIG_BT_LOG_APPL_TRACE_LEVEL" + """Application layer logging""" + + OSI = "CONFIG_BT_LOG_OSI_TRACE_LEVEL" + """OS abstraction layer - Threading, memory, timers""" + + BLUFI = "CONFIG_BT_LOG_BLUFI_TRACE_LEVEL" + """ESP32 WiFi provisioning over Bluetooth""" + + +# Set to track which loggers are needed by components +_required_loggers: set[BTLoggers] = set() + + +def register_bt_logger(*loggers: BTLoggers) -> None: + """Register Bluetooth logger categories that a component needs. + + Args: + *loggers: One or more BTLoggers enum members + """ + for logger in loggers: + if not isinstance(logger, BTLoggers): + raise TypeError( + f"Logger must be a BTLoggers enum member, got {type(logger)}" + ) + _required_loggers.add(logger) + + CONF_BLE_ID = "ble_id" CONF_IO_CAPABILITY = "io_capability" CONF_ADVERTISING_CYCLE_TIME = "advertising_cycle_time" +CONF_DISABLE_BT_LOGS = "disable_bt_logs" NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2] @@ -62,6 +164,9 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional( CONF_ADVERTISING_CYCLE_TIME, default="10s" ): cv.positive_time_period_milliseconds, + cv.SplitDefault(CONF_DISABLE_BT_LOGS, esp32_idf=True): cv.All( + cv.only_with_esp_idf, cv.boolean + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -140,6 +245,16 @@ async def to_code(config): add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) add_idf_sdkconfig_option("CONFIG_BT_BLE_42_FEATURES_SUPPORTED", True) + # Register the core BLE loggers that are always needed + register_bt_logger(BTLoggers.GAP, BTLoggers.BTM, BTLoggers.HCI) + + # Apply logger settings if log disabling is enabled + if config.get(CONF_DISABLE_BT_LOGS, False): + # Disable all Bluetooth loggers that are not required + for logger in BTLoggers: + if logger not in _required_loggers: + add_idf_sdkconfig_option(f"{logger.value}_NONE", True) + cg.add_define("USE_ESP32_BLE") diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index fc1303673f..8adef79d2f 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -28,7 +28,7 @@ static RAMAllocator EVENT_ALLOCATOR( // NOLINT(cppcoreguidelines-avoi void ESP32BLE::setup() { global_ble = this; - ESP_LOGCONFIG(TAG, "Setting up BLE..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!ble_pre_setup_()) { ESP_LOGE(TAG, "BLE could not be prepared for configuration"); @@ -270,14 +270,14 @@ void ESP32BLE::loop() { case BLE_COMPONENT_STATE_DISABLED: return; case BLE_COMPONENT_STATE_DISABLE: { - ESP_LOGD(TAG, "Disabling BLE..."); + ESP_LOGD(TAG, "Disabling"); for (auto *ble_event_handler : this->ble_status_event_handlers_) { ble_event_handler->ble_before_disabled_event_handler(); } if (!ble_dismantle_()) { - ESP_LOGE(TAG, "BLE could not be dismantled"); + ESP_LOGE(TAG, "Could not be dismantled"); this->mark_failed(); return; } @@ -285,11 +285,11 @@ void ESP32BLE::loop() { return; } case BLE_COMPONENT_STATE_ENABLE: { - ESP_LOGD(TAG, "Enabling BLE..."); + ESP_LOGD(TAG, "Enabling"); this->state_ = BLE_COMPONENT_STATE_OFF; if (!ble_setup_()) { - ESP_LOGE(TAG, "BLE could not be set up"); + ESP_LOGE(TAG, "Could not be set up"); this->mark_failed(); return; } @@ -304,20 +304,52 @@ void ESP32BLE::loop() { BLEEvent *ble_event = this->ble_events_.pop(); while (ble_event != nullptr) { switch (ble_event->type_) { - case BLEEvent::GATTS: - this->real_gatts_event_handler_(ble_event->event_.gatts.gatts_event, ble_event->event_.gatts.gatts_if, - &ble_event->event_.gatts.gatts_param); + case BLEEvent::GATTS: { + esp_gatts_cb_event_t event = ble_event->event_.gatts.gatts_event; + esp_gatt_if_t gatts_if = ble_event->event_.gatts.gatts_if; + esp_ble_gatts_cb_param_t *param = ble_event->event_.gatts.gatts_param; + ESP_LOGV(TAG, "gatts_event [esp_gatt_if: %d] - %d", gatts_if, event); + for (auto *gatts_handler : this->gatts_event_handlers_) { + gatts_handler->gatts_event_handler(event, gatts_if, param); + } break; - case BLEEvent::GATTC: - this->real_gattc_event_handler_(ble_event->event_.gattc.gattc_event, ble_event->event_.gattc.gattc_if, - &ble_event->event_.gattc.gattc_param); + } + case BLEEvent::GATTC: { + esp_gattc_cb_event_t event = ble_event->event_.gattc.gattc_event; + esp_gatt_if_t gattc_if = ble_event->event_.gattc.gattc_if; + esp_ble_gattc_cb_param_t *param = ble_event->event_.gattc.gattc_param; + ESP_LOGV(TAG, "gattc_event [esp_gatt_if: %d] - %d", gattc_if, event); + for (auto *gattc_handler : this->gattc_event_handlers_) { + gattc_handler->gattc_event_handler(event, gattc_if, param); + } break; - case BLEEvent::GAP: - this->real_gap_event_handler_(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param); + } + case BLEEvent::GAP: { + esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event; + if (gap_event == ESP_GAP_BLE_SCAN_RESULT_EVT) { + // Use the new scan event handler - no memcpy! + for (auto *scan_handler : this->gap_scan_event_handlers_) { + scan_handler->gap_scan_event_handler(ble_event->scan_result()); + } + } else if (gap_event == ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT || + gap_event == ESP_GAP_BLE_SCAN_START_COMPLETE_EVT || + gap_event == ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT) { + // All three scan complete events have the same structure with just status + // The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe + // This is verified at compile-time by static_assert checks in ble_event.h + // The struct already contains our copy of the status (copied in BLEEvent constructor) + ESP_LOGV(TAG, "gap_event_handler - %d", gap_event); + for (auto *gap_handler : this->gap_event_handlers_) { + gap_handler->gap_event_handler( + gap_event, reinterpret_cast(&ble_event->event_.gap.scan_complete)); + } + } break; + } default: break; } + // Destructor will clean up external allocations for GATTC/GATTS ble_event->~BLEEvent(); EVENT_ALLOCATOR.deallocate(ble_event, 1); ble_event = this->ble_events_.pop(); @@ -325,61 +357,74 @@ void ESP32BLE::loop() { if (this->advertising_ != nullptr) { this->advertising_->loop(); } + + // Log dropped events periodically + size_t dropped = this->ble_events_.get_and_reset_dropped_count(); + if (dropped > 0) { + ESP_LOGW(TAG, "Dropped %zu BLE events due to buffer overflow", dropped); + } } -void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { +template void enqueue_ble_event(Args... args) { + // Check if queue is full before allocating + if (global_ble->ble_events_.full()) { + // Queue is full, drop the event + global_ble->ble_events_.increment_dropped_count(); + return; + } + BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1); if (new_event == nullptr) { // Memory too fragmented to allocate new event. Can only drop it until memory comes back + global_ble->ble_events_.increment_dropped_count(); return; } - new (new_event) BLEEvent(event, param); - global_ble->ble_events_.push(new_event); + new (new_event) BLEEvent(args...); + + // Push the event - since we're the only producer and we checked full() above, + // this should always succeed unless we have a bug + if (!global_ble->ble_events_.push(new_event)) { + // This should not happen in SPSC queue with single producer + ESP_LOGE(TAG, "BLE queue push failed unexpectedly"); + new_event->~BLEEvent(); + EVENT_ALLOCATOR.deallocate(new_event, 1); + } } // NOLINT(clang-analyzer-unix.Malloc) -void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { - ESP_LOGV(TAG, "(BLE) gap_event_handler - %d", event); - for (auto *gap_handler : this->gap_event_handlers_) { - gap_handler->gap_event_handler(event, param); +// Explicit template instantiations for the friend function +template void enqueue_ble_event(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t *); +template void enqueue_ble_event(esp_gatts_cb_event_t, esp_gatt_if_t, esp_ble_gatts_cb_param_t *); +template void enqueue_ble_event(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gattc_cb_param_t *); + +void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { + switch (event) { + // Only queue the 4 GAP events we actually handle + case ESP_GAP_BLE_SCAN_RESULT_EVT: + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: + case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: + enqueue_ble_event(event, param); + return; + + // Ignore these GAP events as they are not relevant for our use case + case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: + case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT: + return; + + default: + break; } + ESP_LOGW(TAG, "Ignoring unexpected GAP event type: %d", event); } void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { - BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1); - if (new_event == nullptr) { - // Memory too fragmented to allocate new event. Can only drop it until memory comes back - return; - } - new (new_event) BLEEvent(event, gatts_if, param); - global_ble->ble_events_.push(new_event); -} // NOLINT(clang-analyzer-unix.Malloc) - -void ESP32BLE::real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t *param) { - ESP_LOGV(TAG, "(BLE) gatts_event [esp_gatt_if: %d] - %d", gatts_if, event); - for (auto *gatts_handler : this->gatts_event_handlers_) { - gatts_handler->gatts_event_handler(event, gatts_if, param); - } + enqueue_ble_event(event, gatts_if, param); } void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { - BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1); - if (new_event == nullptr) { - // Memory too fragmented to allocate new event. Can only drop it until memory comes back - return; - } - new (new_event) BLEEvent(event, gattc_if, param); - global_ble->ble_events_.push(new_event); -} // NOLINT(clang-analyzer-unix.Malloc) - -void ESP32BLE::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param) { - ESP_LOGV(TAG, "(BLE) gattc_event [esp_gatt_if: %d] - %d", gattc_if, event); - for (auto *gattc_handler : this->gattc_event_handlers_) { - gattc_handler->gattc_event_handler(event, gattc_if, param); - } + enqueue_ble_event(event, gattc_if, param); } float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; } @@ -408,10 +453,12 @@ void ESP32BLE::dump_config() { io_capability_s = "invalid"; break; } - ESP_LOGCONFIG(TAG, "ESP32 BLE:"); - ESP_LOGCONFIG(TAG, " MAC address: %02X:%02X:%02X:%02X:%02X:%02X", mac_address[0], mac_address[1], mac_address[2], - mac_address[3], mac_address[4], mac_address[5]); - ESP_LOGCONFIG(TAG, " IO Capability: %s", io_capability_s); + ESP_LOGCONFIG(TAG, + "ESP32 BLE:\n" + " MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n" + " IO Capability: %s", + mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5], + io_capability_s); } else { ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled"); } diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 13ec3b6dd9..58c064a2ef 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -2,6 +2,7 @@ #include "ble_advertising.h" #include "ble_uuid.h" +#include "ble_scan_result.h" #include @@ -22,6 +23,16 @@ namespace esphome { namespace esp32_ble { +// Maximum number of BLE scan results to buffer +#ifdef USE_PSRAM +static constexpr uint8_t SCAN_RESULT_BUFFER_SIZE = 32; +#else +static constexpr uint8_t SCAN_RESULT_BUFFER_SIZE = 20; +#endif + +// Maximum size of the BLE event queue - must be power of 2 for lock-free queue +static constexpr size_t MAX_BLE_QUEUE_SIZE = 64; + uint64_t ble_addr_to_uint64(const esp_bd_addr_t address); // NOLINTNEXTLINE(modernize-use-using) @@ -57,6 +68,11 @@ class GAPEventHandler { virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0; }; +class GAPScanEventHandler { + public: + virtual void gap_scan_event_handler(const BLEScanResult &scan_result) = 0; +}; + class GATTcEventHandler { public: virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, @@ -101,6 +117,9 @@ class ESP32BLE : public Component { void advertising_register_raw_advertisement_callback(std::function &&callback); void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); } + void register_gap_scan_event_handler(GAPScanEventHandler *handler) { + this->gap_scan_event_handlers_.push_back(handler); + } void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); } void register_gatts_event_handler(GATTsEventHandler *handler) { this->gatts_event_handlers_.push_back(handler); } void register_ble_status_event_handler(BLEStatusEventHandler *handler) { @@ -113,22 +132,22 @@ class ESP32BLE : public Component { static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); - void real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); - void real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); - void real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); - bool ble_setup_(); bool ble_dismantle_(); bool ble_pre_setup_(); void advertising_init_(); + private: + template friend void enqueue_ble_event(Args... args); + std::vector gap_event_handlers_; + std::vector gap_scan_event_handlers_; std::vector gattc_event_handlers_; std::vector gatts_event_handlers_; std::vector ble_status_event_handlers_; BLEComponentState state_{BLE_COMPONENT_STATE_OFF}; - Queue ble_events_; + LockFreeQueue ble_events_; BLEAdvertising *advertising_{}; esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE}; uint32_t advertising_cycle_time_{}; diff --git a/esphome/components/esp32_ble/ble_event.h b/esphome/components/esp32_ble/ble_event.h index 1cf63b2fab..f51095effd 100644 --- a/esphome/components/esp32_ble/ble_event.h +++ b/esphome/components/esp32_ble/ble_event.h @@ -2,92 +2,232 @@ #ifdef USE_ESP32 +#include // for offsetof #include #include #include #include +#include "ble_scan_result.h" + namespace esphome { namespace esp32_ble { + +// Compile-time verification that ESP-IDF scan complete events only contain a status field +// This ensures our reinterpret_cast in ble.cpp is safe +static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param) == sizeof(esp_bt_status_t), + "ESP-IDF scan_param_cmpl structure has unexpected size"); +static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param) == sizeof(esp_bt_status_t), + "ESP-IDF scan_start_cmpl structure has unexpected size"); +static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param) == sizeof(esp_bt_status_t), + "ESP-IDF scan_stop_cmpl structure has unexpected size"); + +// Verify the status field is at offset 0 (first member) +static_assert(offsetof(esp_ble_gap_cb_param_t, scan_param_cmpl.status) == + offsetof(esp_ble_gap_cb_param_t, scan_param_cmpl), + "status must be first member of scan_param_cmpl"); +static_assert(offsetof(esp_ble_gap_cb_param_t, scan_start_cmpl.status) == + offsetof(esp_ble_gap_cb_param_t, scan_start_cmpl), + "status must be first member of scan_start_cmpl"); +static_assert(offsetof(esp_ble_gap_cb_param_t, scan_stop_cmpl.status) == + offsetof(esp_ble_gap_cb_param_t, scan_stop_cmpl), + "status must be first member of scan_stop_cmpl"); + // Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop(). -// This class stores each event in a single type. +// This class stores each event with minimal memory usage. +// GAP events (99% of traffic) don't have the vector overhead. +// GATTC/GATTS events use heap allocation for their param and data. +// +// Event flow: +// 1. ESP-IDF BLE stack calls our static handlers in the BLE task context +// 2. The handlers create a BLEEvent instance, copying only the data we need +// 3. The event is pushed to a thread-safe queue +// 4. In the main loop(), events are popped from the queue and processed +// 5. The event destructor cleans up any external allocations +// +// Thread safety: +// - GAP events: We copy only the fields we need directly into the union +// - GATTC/GATTS events: We heap-allocate and copy the entire param struct, ensuring +// the data remains valid even after the BLE callback returns. The original +// param pointer from ESP-IDF is only valid during the callback. class BLEEvent { public: - BLEEvent(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) { - this->event_.gap.gap_event = e; - memcpy(&this->event_.gap.gap_param, p, sizeof(esp_ble_gap_cb_param_t)); - this->type_ = GAP; - }; - - BLEEvent(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) { - this->event_.gattc.gattc_event = e; - this->event_.gattc.gattc_if = i; - memcpy(&this->event_.gattc.gattc_param, p, sizeof(esp_ble_gattc_cb_param_t)); - // Need to also make a copy of relevant event data. - switch (e) { - case ESP_GATTC_NOTIFY_EVT: - this->data.assign(p->notify.value, p->notify.value + p->notify.value_len); - this->event_.gattc.gattc_param.notify.value = this->data.data(); - break; - case ESP_GATTC_READ_CHAR_EVT: - case ESP_GATTC_READ_DESCR_EVT: - this->data.assign(p->read.value, p->read.value + p->read.value_len); - this->event_.gattc.gattc_param.read.value = this->data.data(); - break; - default: - break; - } - this->type_ = GATTC; - }; - - BLEEvent(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) { - this->event_.gatts.gatts_event = e; - this->event_.gatts.gatts_if = i; - memcpy(&this->event_.gatts.gatts_param, p, sizeof(esp_ble_gatts_cb_param_t)); - // Need to also make a copy of relevant event data. - switch (e) { - case ESP_GATTS_WRITE_EVT: - this->data.assign(p->write.value, p->write.value + p->write.len); - this->event_.gatts.gatts_param.write.value = this->data.data(); - break; - default: - break; - } - this->type_ = GATTS; - }; - - union { - // NOLINTNEXTLINE(readability-identifier-naming) - struct gap_event { - esp_gap_ble_cb_event_t gap_event; - esp_ble_gap_cb_param_t gap_param; - } gap; - - // NOLINTNEXTLINE(readability-identifier-naming) - struct gattc_event { - esp_gattc_cb_event_t gattc_event; - esp_gatt_if_t gattc_if; - esp_ble_gattc_cb_param_t gattc_param; - } gattc; - - // NOLINTNEXTLINE(readability-identifier-naming) - struct gatts_event { - esp_gatts_cb_event_t gatts_event; - esp_gatt_if_t gatts_if; - esp_ble_gatts_cb_param_t gatts_param; - } gatts; - } event_; - - std::vector data{}; // NOLINTNEXTLINE(readability-identifier-naming) enum ble_event_t : uint8_t { GAP, GATTC, GATTS, - } type_; + }; + + // Constructor for GAP events - no external allocations needed + BLEEvent(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) { + this->type_ = GAP; + this->event_.gap.gap_event = e; + + if (p == nullptr) { + return; // Invalid event, but we can't log in header file + } + + // Only copy the data we actually use for each GAP event type + switch (e) { + case ESP_GAP_BLE_SCAN_RESULT_EVT: + // Copy only the fields we use from scan results + memcpy(this->event_.gap.scan_result.bda, p->scan_rst.bda, sizeof(esp_bd_addr_t)); + this->event_.gap.scan_result.ble_addr_type = p->scan_rst.ble_addr_type; + this->event_.gap.scan_result.rssi = p->scan_rst.rssi; + this->event_.gap.scan_result.adv_data_len = p->scan_rst.adv_data_len; + this->event_.gap.scan_result.scan_rsp_len = p->scan_rst.scan_rsp_len; + this->event_.gap.scan_result.search_evt = p->scan_rst.search_evt; + memcpy(this->event_.gap.scan_result.ble_adv, p->scan_rst.ble_adv, + ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX); + break; + + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: + this->event_.gap.scan_complete.status = p->scan_param_cmpl.status; + break; + + case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: + this->event_.gap.scan_complete.status = p->scan_start_cmpl.status; + break; + + case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: + this->event_.gap.scan_complete.status = p->scan_stop_cmpl.status; + break; + + default: + // We only handle 4 GAP event types, others are dropped + break; + } + } + + // Constructor for GATTC events - uses heap allocation + // Creates a copy of the param struct since the original is only valid during the callback + BLEEvent(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) { + this->type_ = GATTC; + this->event_.gattc.gattc_event = e; + this->event_.gattc.gattc_if = i; + + if (p == nullptr) { + this->event_.gattc.gattc_param = nullptr; + this->event_.gattc.data = nullptr; + return; // Invalid event, but we can't log in header file + } + + // Heap-allocate param and data + // Heap allocation is used because GATTC/GATTS events are rare (<1% of events) + // while GAP events (99%) are stored inline to minimize memory usage + this->event_.gattc.gattc_param = new esp_ble_gattc_cb_param_t(*p); + + // Copy data for events that need it + switch (e) { + case ESP_GATTC_NOTIFY_EVT: + this->event_.gattc.data = new std::vector(p->notify.value, p->notify.value + p->notify.value_len); + this->event_.gattc.gattc_param->notify.value = this->event_.gattc.data->data(); + break; + case ESP_GATTC_READ_CHAR_EVT: + case ESP_GATTC_READ_DESCR_EVT: + this->event_.gattc.data = new std::vector(p->read.value, p->read.value + p->read.value_len); + this->event_.gattc.gattc_param->read.value = this->event_.gattc.data->data(); + break; + default: + this->event_.gattc.data = nullptr; + break; + } + } + + // Constructor for GATTS events - uses heap allocation + // Creates a copy of the param struct since the original is only valid during the callback + BLEEvent(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) { + this->type_ = GATTS; + this->event_.gatts.gatts_event = e; + this->event_.gatts.gatts_if = i; + + if (p == nullptr) { + this->event_.gatts.gatts_param = nullptr; + this->event_.gatts.data = nullptr; + return; // Invalid event, but we can't log in header file + } + + // Heap-allocate param and data + // Heap allocation is used because GATTC/GATTS events are rare (<1% of events) + // while GAP events (99%) are stored inline to minimize memory usage + this->event_.gatts.gatts_param = new esp_ble_gatts_cb_param_t(*p); + + // Copy data for events that need it + switch (e) { + case ESP_GATTS_WRITE_EVT: + this->event_.gatts.data = new std::vector(p->write.value, p->write.value + p->write.len); + this->event_.gatts.gatts_param->write.value = this->event_.gatts.data->data(); + break; + default: + this->event_.gatts.data = nullptr; + break; + } + } + + // Destructor to clean up heap allocations + ~BLEEvent() { + switch (this->type_) { + case GATTC: + delete this->event_.gattc.gattc_param; + delete this->event_.gattc.data; + break; + case GATTS: + delete this->event_.gatts.gatts_param; + delete this->event_.gatts.data; + break; + default: + break; + } + } + + // Disable copy to prevent double-delete + BLEEvent(const BLEEvent &) = delete; + BLEEvent &operator=(const BLEEvent &) = delete; + + union { + // NOLINTNEXTLINE(readability-identifier-naming) + struct gap_event { + esp_gap_ble_cb_event_t gap_event; + union { + BLEScanResult scan_result; // 73 bytes + // This matches ESP-IDF's scan complete event structures + // All three (scan_param_cmpl, scan_start_cmpl, scan_stop_cmpl) have identical layout + struct { + esp_bt_status_t status; + } scan_complete; // 1 byte + }; + } gap; // 80 bytes total + + // NOLINTNEXTLINE(readability-identifier-naming) + struct gattc_event { + esp_gattc_cb_event_t gattc_event; + esp_gatt_if_t gattc_if; + esp_ble_gattc_cb_param_t *gattc_param; // Heap-allocated + std::vector *data; // Heap-allocated + } gattc; // 16 bytes (pointers only) + + // NOLINTNEXTLINE(readability-identifier-naming) + struct gatts_event { + esp_gatts_cb_event_t gatts_event; + esp_gatt_if_t gatts_if; + esp_ble_gatts_cb_param_t *gatts_param; // Heap-allocated + std::vector *data; // Heap-allocated + } gatts; // 16 bytes (pointers only) + } event_; // 80 bytes + + ble_event_t type_; + + // Helper methods to access event data + ble_event_t type() const { return type_; } + esp_gap_ble_cb_event_t gap_event_type() const { return event_.gap.gap_event; } + const BLEScanResult &scan_result() const { return event_.gap.scan_result; } + esp_bt_status_t scan_complete_status() const { return event_.gap.scan_complete.status; } }; +// BLEEvent total size: 84 bytes (80 byte union + 1 byte type + 3 bytes padding) + } // namespace esp32_ble } // namespace esphome diff --git a/esphome/components/esp32_ble/ble_scan_result.h b/esphome/components/esp32_ble/ble_scan_result.h new file mode 100644 index 0000000000..49b0d5523d --- /dev/null +++ b/esphome/components/esp32_ble/ble_scan_result.h @@ -0,0 +1,24 @@ +#pragma once + +#ifdef USE_ESP32 + +#include + +namespace esphome { +namespace esp32_ble { + +// Structure for BLE scan results - only fields we actually use +struct __attribute__((packed)) BLEScanResult { + esp_bd_addr_t bda; + uint8_t ble_addr_type; + int8_t rssi; + uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX]; + uint8_t adv_data_len; + uint8_t scan_rsp_len; + uint8_t search_evt; +}; // ~73 bytes vs ~400 bytes for full esp_ble_gap_cb_param_t + +} // namespace esp32_ble +} // namespace esphome + +#endif diff --git a/esphome/components/esp32_ble/ble_uuid.h b/esphome/components/esp32_ble/ble_uuid.h index d90db3a599..06f84d4da7 100644 --- a/esphome/components/esp32_ble/ble_uuid.h +++ b/esphome/components/esp32_ble/ble_uuid.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #ifdef USE_ESP32 diff --git a/esphome/components/esp32_ble/queue.h b/esphome/components/esp32_ble/queue.h index c98477e121..56d2efd18b 100644 --- a/esphome/components/esp32_ble/queue.h +++ b/esphome/components/esp32_ble/queue.h @@ -2,52 +2,78 @@ #ifdef USE_ESP32 -#include -#include - -#include -#include +#include +#include /* * BLE events come in from a separate Task (thread) in the ESP32 stack. Rather - * than trying to deal with various locking strategies, all incoming GAP and GATT - * events will simply be placed on a semaphore guarded queue. The next time the - * component runs loop(), these events are popped off the queue and handed at - * this safer time. + * than using mutex-based locking, this lock-free queue allows the BLE + * task to enqueue events without blocking. The main loop() then processes + * these events at a safer time. + * + * This is a Single-Producer Single-Consumer (SPSC) lock-free ring buffer. + * The BLE task is the only producer, and the main loop() is the only consumer. */ namespace esphome { namespace esp32_ble { -template class Queue { +template class LockFreeQueue { public: - Queue() { m_ = xSemaphoreCreateMutex(); } + LockFreeQueue() : head_(0), tail_(0), dropped_count_(0) {} - void push(T *element) { + bool push(T *element) { if (element == nullptr) - return; - // It is not called from main loop. Thus it won't block main thread. - xSemaphoreTake(m_, portMAX_DELAY); - q_.push(element); - xSemaphoreGive(m_); + return false; + + size_t current_tail = tail_.load(std::memory_order_relaxed); + size_t next_tail = (current_tail + 1) % SIZE; + + if (next_tail == head_.load(std::memory_order_acquire)) { + // Buffer full + dropped_count_.fetch_add(1, std::memory_order_relaxed); + return false; + } + + buffer_[current_tail] = element; + tail_.store(next_tail, std::memory_order_release); + return true; } T *pop() { - T *element = nullptr; + size_t current_head = head_.load(std::memory_order_relaxed); - if (xSemaphoreTake(m_, 5L / portTICK_PERIOD_MS)) { - if (!q_.empty()) { - element = q_.front(); - q_.pop(); - } - xSemaphoreGive(m_); + if (current_head == tail_.load(std::memory_order_acquire)) { + return nullptr; // Empty } + + T *element = buffer_[current_head]; + head_.store((current_head + 1) % SIZE, std::memory_order_release); return element; } + size_t size() const { + size_t tail = tail_.load(std::memory_order_acquire); + size_t head = head_.load(std::memory_order_acquire); + return (tail - head + SIZE) % SIZE; + } + + size_t get_and_reset_dropped_count() { return dropped_count_.exchange(0, std::memory_order_relaxed); } + + void increment_dropped_count() { dropped_count_.fetch_add(1, std::memory_order_relaxed); } + + bool empty() const { return head_.load(std::memory_order_acquire) == tail_.load(std::memory_order_acquire); } + + bool full() const { + size_t next_tail = (tail_.load(std::memory_order_relaxed) + 1) % SIZE; + return next_tail == head_.load(std::memory_order_acquire); + } + protected: - std::queue q_; - SemaphoreHandle_t m_; + T *buffer_[SIZE]; + std::atomic head_; + std::atomic tail_; + std::atomic dropped_count_; }; } // namespace esp32_ble diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index 2a1757524f..4e61fb287c 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -45,8 +45,10 @@ void BLEClientBase::loop() { float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } void BLEClientBase::dump_config() { - ESP_LOGCONFIG(TAG, " Address: %s", this->address_str().c_str()); - ESP_LOGCONFIG(TAG, " Auto-Connect: %s", TRUEFALSE(this->auto_connect_)); + ESP_LOGCONFIG(TAG, + " Address: %s\n" + " Auto-Connect: %s", + this->address_str().c_str(), TRUEFALSE(this->auto_connect_)); std::string state_name; switch (this->state()) { case espbt::ClientState::INIT: diff --git a/esphome/components/esp32_ble_server/__init__.py b/esphome/components/esp32_ble_server/__init__.py index 0fcb5c9822..773445a1a7 100644 --- a/esphome/components/esp32_ble_server/__init__.py +++ b/esphome/components/esp32_ble_server/__init__.py @@ -4,7 +4,7 @@ from esphome import automation import esphome.codegen as cg from esphome.components import esp32_ble from esphome.components.esp32 import add_idf_sdkconfig_option -from esphome.components.esp32_ble import bt_uuid +from esphome.components.esp32_ble import BTLoggers, bt_uuid import esphome.config_validation as cv from esphome.config_validation import UNDEFINED from esphome.const import ( @@ -525,6 +525,9 @@ async def to_code_characteristic(service_var, char_conf): async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.SMP) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/esp32_ble_server/ble_characteristic.cpp b/esphome/components/esp32_ble_server/ble_characteristic.cpp index 15739d60bb..373d57436e 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.cpp +++ b/esphome/components/esp32_ble_server/ble_characteristic.cpp @@ -2,6 +2,7 @@ #include "ble_server.h" #include "ble_service.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" #ifdef USE_ESP32 diff --git a/esphome/components/esp32_ble_server/ble_server.h b/esphome/components/esp32_ble_server/ble_server.h index 43599438f3..531b52d6b9 100644 --- a/esphome/components/esp32_ble_server/ble_server.h +++ b/esphome/components/esp32_ble_server/ble_server.h @@ -43,7 +43,6 @@ class BLEServer : public Component, float get_setup_priority() const override; bool can_proceed() override; - void teardown(); bool is_running(); void set_manufacturer_data(const std::vector &data) { diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index a4425b9680..2242d709a4 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -1,14 +1,15 @@ from __future__ import annotations -from collections.abc import MutableMapping +from collections.abc import Callable, MutableMapping import logging -from typing import Any, Callable +from typing import Any from esphome import automation import esphome.codegen as cg from esphome.components import esp32_ble from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.components.esp32_ble import ( + BTLoggers, bt_uuid, bt_uuid16_format, bt_uuid32_format, @@ -259,11 +260,15 @@ ESP_BLE_DEVICE_SCHEMA = cv.Schema( async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.BLE_SCAN) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID]) cg.add(parent.register_gap_event_handler(var)) + cg.add(parent.register_gap_scan_event_handler(var)) cg.add(parent.register_gattc_event_handler(var)) cg.add(parent.register_ble_status_event_handler(var)) cg.add(var.set_parent(parent)) diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 1a6071c9fe..c5906779f1 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -50,17 +50,15 @@ void ESP32BLETracker::setup() { ESP_LOGE(TAG, "BLE Tracker was marked failed by ESP32BLE"); return; } - ExternalRAMAllocator allocator( - ExternalRAMAllocator::ALLOW_FAILURE); - this->scan_result_buffer_ = allocator.allocate(ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE); + RAMAllocator allocator; + this->scan_ring_buffer_ = allocator.allocate(SCAN_RESULT_BUFFER_SIZE); - if (this->scan_result_buffer_ == nullptr) { - ESP_LOGE(TAG, "Could not allocate buffer for BLE Tracker!"); + if (this->scan_ring_buffer_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate ring buffer for BLE Tracker!"); this->mark_failed(); } global_esp32_ble_tracker = this; - this->scan_result_lock_ = xSemaphoreCreateMutex(); #ifdef USE_OTA ota::get_global_ota_callback()->add_on_state_callback( @@ -120,27 +118,31 @@ void ESP32BLETracker::loop() { } bool promote_to_connecting = discovered && !searching && !connecting; - if (this->scanner_state_ == ScannerState::RUNNING && - this->scan_result_index_ && // if it looks like we have a scan result we will take the lock - xSemaphoreTake(this->scan_result_lock_, 0)) { - uint32_t index = this->scan_result_index_; - if (index >= ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) { - ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up."); - } + // Process scan results from lock-free SPSC ring buffer + // Consumer side: This runs in the main loop thread + if (this->scanner_state_ == ScannerState::RUNNING) { + // Load our own index with relaxed ordering (we're the only writer) + size_t read_idx = this->ring_read_index_.load(std::memory_order_relaxed); - if (this->raw_advertisements_) { - for (auto *listener : this->listeners_) { - listener->parse_devices(this->scan_result_buffer_, this->scan_result_index_); - } - for (auto *client : this->clients_) { - client->parse_devices(this->scan_result_buffer_, this->scan_result_index_); - } - } + // Load producer's index with acquire to see their latest writes + size_t write_idx = this->ring_write_index_.load(std::memory_order_acquire); - if (this->parse_advertisements_) { - for (size_t i = 0; i < index; i++) { + while (read_idx != write_idx) { + // Process one result at a time directly from ring buffer + BLEScanResult &scan_result = this->scan_ring_buffer_[read_idx]; + + if (this->raw_advertisements_) { + for (auto *listener : this->listeners_) { + listener->parse_devices(&scan_result, 1); + } + for (auto *client : this->clients_) { + client->parse_devices(&scan_result, 1); + } + } + + if (this->parse_advertisements_) { ESPBTDevice device; - device.parse_scan_rst(this->scan_result_buffer_[i]); + device.parse_scan_rst(scan_result); bool found = false; for (auto *listener : this->listeners_) { @@ -161,9 +163,19 @@ void ESP32BLETracker::loop() { this->print_bt_device_info(device); } } + + // Move to next entry in ring buffer + read_idx = (read_idx + 1) % SCAN_RESULT_BUFFER_SIZE; + + // Store with release to ensure reads complete before index update + this->ring_read_index_.store(read_idx, std::memory_order_release); + } + + // Log dropped results periodically + size_t dropped = this->scan_results_dropped_.exchange(0, std::memory_order_relaxed); + if (dropped > 0) { + ESP_LOGW(TAG, "Dropped %zu BLE scan results due to buffer overflow", dropped); } - this->scan_result_index_ = 0; - xSemaphoreGive(this->scan_result_lock_); } if (this->scanner_state_ == ScannerState::STOPPED) { this->end_of_scan_(); // Change state to IDLE @@ -172,7 +184,7 @@ void ESP32BLETracker::loop() { (this->scan_set_param_failed_ && this->scanner_state_ == ScannerState::RUNNING)) { this->stop_scan_(); if (this->scan_start_fail_count_ == std::numeric_limits::max()) { - ESP_LOGE(TAG, "ESP-IDF BLE scan could not restart after %d attempts, rebooting to restore BLE stack...", + ESP_LOGE(TAG, "Scan could not restart after %d attempts, rebooting to restore stack (IDF)", std::numeric_limits::max()); App.reboot(); } @@ -219,10 +231,10 @@ void ESP32BLETracker::loop() { for (auto *client : this->clients_) { if (client->state() == ClientState::DISCOVERED) { if (this->scanner_state_ == ScannerState::RUNNING) { - ESP_LOGD(TAG, "Stopping scan to make connection..."); + ESP_LOGD(TAG, "Stopping scan to make connection"); this->stop_scan_(); } else if (this->scanner_state_ == ScannerState::IDLE) { - ESP_LOGD(TAG, "Promoting client to connect..."); + ESP_LOGD(TAG, "Promoting client to connect"); // We only want to promote one client at a time. // once the scanner is fully stopped. #ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE @@ -306,7 +318,7 @@ void ESP32BLETracker::start_scan_(bool first) { // Start timeout before scan is started. Otherwise scan never starts if any error. this->set_timeout("scan", this->scan_duration_ * 2000, []() { - ESP_LOGE(TAG, "ESP-IDF BLE scan never terminated, rebooting to restore BLE stack..."); + ESP_LOGE(TAG, "Scan never terminated, rebooting to restore stack (IDF)"); App.reboot(); }); @@ -370,9 +382,6 @@ void ESP32BLETracker::recalculate_advertisement_parser_types() { void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { - case ESP_GAP_BLE_SCAN_RESULT_EVT: - this->gap_scan_result_(param->scan_rst); - break; case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: this->gap_scan_set_param_complete_(param->scan_param_cmpl); break; @@ -385,11 +394,57 @@ void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_ga default: break; } + // Forward all events to clients (scan results are handled separately via gap_scan_event_handler) for (auto *client : this->clients_) { client->gap_event_handler(event, param); } } +void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) { + ESP_LOGV(TAG, "gap_scan_result - event %d", scan_result.search_evt); + + if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) { + // Lock-free SPSC ring buffer write (Producer side) + // This runs in the ESP-IDF Bluetooth stack callback thread + // IMPORTANT: Only this thread writes to ring_write_index_ + + // Load our own index with relaxed ordering (we're the only writer) + size_t write_idx = this->ring_write_index_.load(std::memory_order_relaxed); + size_t next_write_idx = (write_idx + 1) % SCAN_RESULT_BUFFER_SIZE; + + // Load consumer's index with acquire to see their latest updates + size_t read_idx = this->ring_read_index_.load(std::memory_order_acquire); + + // Check if buffer is full + if (next_write_idx != read_idx) { + // Write to ring buffer + this->scan_ring_buffer_[write_idx] = scan_result; + + // Store with release to ensure the write is visible before index update + this->ring_write_index_.store(next_write_idx, std::memory_order_release); + } else { + // Buffer full, track dropped results + this->scan_results_dropped_.fetch_add(1, std::memory_order_relaxed); + } + } else if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) { + // Scan finished on its own + if (this->scanner_state_ != ScannerState::RUNNING) { + if (this->scanner_state_ == ScannerState::STOPPING) { + ESP_LOGE(TAG, "Scan was not running when scan completed."); + } else if (this->scanner_state_ == ScannerState::STARTING) { + ESP_LOGE(TAG, "Scan was not started when scan completed."); + } else if (this->scanner_state_ == ScannerState::FAILED) { + ESP_LOGE(TAG, "Scan was in failed state when scan completed."); + } else if (this->scanner_state_ == ScannerState::IDLE) { + ESP_LOGE(TAG, "Scan was idle when scan completed."); + } else if (this->scanner_state_ == ScannerState::STOPPED) { + ESP_LOGE(TAG, "Scan was stopped when scan completed."); + } + } + this->set_scanner_state_(ScannerState::STOPPED); + } +} + void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param ¶m) { ESP_LOGV(TAG, "gap_scan_set_param_complete - status %d", param.status); if (param.status == ESP_BT_STATUS_DONE) { @@ -444,34 +499,6 @@ void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_ this->set_scanner_state_(ScannerState::STOPPED); } -void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { - ESP_LOGV(TAG, "gap_scan_result - event %d", param.search_evt); - if (param.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) { - if (xSemaphoreTake(this->scan_result_lock_, 0)) { - if (this->scan_result_index_ < ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) { - this->scan_result_buffer_[this->scan_result_index_++] = param; - } - xSemaphoreGive(this->scan_result_lock_); - } - } else if (param.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) { - // Scan finished on its own - if (this->scanner_state_ != ScannerState::RUNNING) { - if (this->scanner_state_ == ScannerState::STOPPING) { - ESP_LOGE(TAG, "Scan was not running when scan completed."); - } else if (this->scanner_state_ == ScannerState::STARTING) { - ESP_LOGE(TAG, "Scan was not started when scan completed."); - } else if (this->scanner_state_ == ScannerState::FAILED) { - ESP_LOGE(TAG, "Scan was in failed state when scan completed."); - } else if (this->scanner_state_ == ScannerState::IDLE) { - ESP_LOGE(TAG, "Scan was idle when scan completed."); - } else if (this->scanner_state_ == ScannerState::STOPPED) { - ESP_LOGE(TAG, "Scan was stopped when scan completed."); - } - } - this->set_scanner_state_(ScannerState::STOPPED); - } -} - void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { for (auto *client : this->clients_) { @@ -494,13 +521,15 @@ optional ESPBLEiBeacon::from_manufacturer_data(const ServiceData return ESPBLEiBeacon(data.data.data()); } -void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { - this->scan_result_ = param; +void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) { for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++) - this->address_[i] = param.bda[i]; - this->address_type_ = param.ble_addr_type; - this->rssi_ = param.rssi; - this->parse_adv_(param); + this->address_[i] = scan_result.bda[i]; + this->address_type_ = static_cast(scan_result.ble_addr_type); + this->rssi_ = scan_result.rssi; + + // Parse advertisement data directly + uint8_t total_len = scan_result.adv_data_len + scan_result.scan_rsp_len; + this->parse_adv_(scan_result.ble_adv, total_len); #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE ESP_LOGVV(TAG, "Parse Result:"); @@ -558,13 +587,13 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e ESP_LOGVV(TAG, " Data: %s", format_hex_pretty(data.data).c_str()); } - ESP_LOGVV(TAG, " Adv data: %s", format_hex_pretty(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str()); + ESP_LOGVV(TAG, " Adv data: %s", + format_hex_pretty(scan_result.ble_adv, scan_result.adv_data_len + scan_result.scan_rsp_len).c_str()); #endif } -void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { + +void ESPBTDevice::parse_adv_(const uint8_t *payload, uint8_t len) { size_t offset = 0; - const uint8_t *payload = param.ble_adv; - uint8_t len = param.adv_data_len + param.scan_rsp_len; while (offset + 2 < len) { const uint8_t field_length = payload[offset++]; // First byte is length of adv record @@ -731,11 +760,14 @@ uint64_t ESPBTDevice::address_uint64() const { return esp32_ble::ble_addr_to_uin void ESP32BLETracker::dump_config() { ESP_LOGCONFIG(TAG, "BLE Tracker:"); - ESP_LOGCONFIG(TAG, " Scan Duration: %" PRIu32 " s", this->scan_duration_); - ESP_LOGCONFIG(TAG, " Scan Interval: %.1f ms", this->scan_interval_ * 0.625f); - ESP_LOGCONFIG(TAG, " Scan Window: %.1f ms", this->scan_window_ * 0.625f); - ESP_LOGCONFIG(TAG, " Scan Type: %s", this->scan_active_ ? "ACTIVE" : "PASSIVE"); - ESP_LOGCONFIG(TAG, " Continuous Scanning: %s", YESNO(this->scan_continuous_)); + ESP_LOGCONFIG(TAG, + " Scan Duration: %" PRIu32 " s\n" + " Scan Interval: %.1f ms\n" + " Scan Window: %.1f ms\n" + " Scan Type: %s\n" + " Continuous Scanning: %s", + this->scan_duration_, this->scan_interval_ * 0.625f, this->scan_window_ * 0.625f, + this->scan_active_ ? "ACTIVE" : "PASSIVE", YESNO(this->scan_continuous_)); switch (this->scanner_state_) { case ScannerState::IDLE: ESP_LOGCONFIG(TAG, " Scanner State: IDLE"); diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index eea73a7d26..16a100fb47 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -6,6 +6,7 @@ #include "esphome/core/helpers.h" #include +#include #include #include @@ -62,7 +63,7 @@ class ESPBLEiBeacon { class ESPBTDevice { public: - void parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m); + void parse_scan_rst(const BLEScanResult &scan_result); std::string address_str() const; @@ -84,8 +85,6 @@ class ESPBTDevice { const std::vector &get_service_datas() const { return service_datas_; } - const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &get_scan_result() const { return scan_result_; } - bool resolve_irk(const uint8_t *irk) const; optional get_ibeacon() const { @@ -98,7 +97,7 @@ class ESPBTDevice { } protected: - void parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m); + void parse_adv_(const uint8_t *payload, uint8_t len); esp_bd_addr_t address_{ 0, @@ -112,7 +111,6 @@ class ESPBTDevice { std::vector service_uuids_{}; std::vector manufacturer_datas_{}; std::vector service_datas_{}; - esp_ble_gap_cb_param_t::ble_scan_result_evt_param scan_result_{}; }; class ESP32BLETracker; @@ -121,9 +119,7 @@ class ESPBTDeviceListener { public: virtual void on_scan_end() {} virtual bool parse_device(const ESPBTDevice &device) = 0; - virtual bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) { - return false; - }; + virtual bool parse_devices(const BLEScanResult *scan_results, size_t count) { return false; }; virtual AdvertisementParserType get_advertisement_parser_type() { return AdvertisementParserType::PARSED_ADVERTISEMENTS; }; @@ -210,6 +206,7 @@ class ESPBTClient : public ESPBTDeviceListener { class ESP32BLETracker : public Component, public GAPEventHandler, + public GAPScanEventHandler, public GATTcEventHandler, public BLEStatusEventHandler, public Parented { @@ -240,6 +237,7 @@ class ESP32BLETracker : public Component, void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override; void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; + void gap_scan_event_handler(const BLEScanResult &scan_result) override; void ble_before_disabled_event_handler() override; void add_scanner_state_callback(std::function &&callback) { @@ -285,14 +283,16 @@ class ESP32BLETracker : public Component, bool ble_was_disabled_{true}; bool raw_advertisements_{false}; bool parse_advertisements_{false}; - SemaphoreHandle_t scan_result_lock_; - size_t scan_result_index_{0}; -#ifdef USE_PSRAM - const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 32; -#else - const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 20; -#endif // USE_PSRAM - esp_ble_gap_cb_param_t::ble_scan_result_evt_param *scan_result_buffer_; + + // Lock-free Single-Producer Single-Consumer (SPSC) ring buffer for scan results + // Producer: ESP-IDF Bluetooth stack callback (gap_scan_event_handler) + // Consumer: ESPHome main loop (loop() method) + // This design ensures zero blocking in the BT callback and prevents scan result loss + BLEScanResult *scan_ring_buffer_; + std::atomic ring_write_index_{0}; // Written only by BT callback (producer) + std::atomic ring_read_index_{0}; // Written only by main loop (consumer) + std::atomic scan_results_dropped_{0}; // Tracks buffer overflow events + esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS}; esp_bt_status_t scan_set_param_failed_{ESP_BT_STATUS_SUCCESS}; int connecting_{0}; diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index cfcf7869d4..da0f277358 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -46,17 +46,20 @@ void ESP32Camera::setup() { void ESP32Camera::dump_config() { auto conf = this->config_; - ESP_LOGCONFIG(TAG, "ESP32 Camera:"); - ESP_LOGCONFIG(TAG, " Name: %s", this->name_.c_str()); - ESP_LOGCONFIG(TAG, " Internal: %s", YESNO(this->internal_)); - ESP_LOGCONFIG(TAG, " Data Pins: D0:%d D1:%d D2:%d D3:%d D4:%d D5:%d D6:%d D7:%d", conf.pin_d0, conf.pin_d1, - conf.pin_d2, conf.pin_d3, conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7); - ESP_LOGCONFIG(TAG, " VSYNC Pin: %d", conf.pin_vsync); - ESP_LOGCONFIG(TAG, " HREF Pin: %d", conf.pin_href); - ESP_LOGCONFIG(TAG, " Pixel Clock Pin: %d", conf.pin_pclk); - ESP_LOGCONFIG(TAG, " External Clock: Pin:%d Frequency:%u", conf.pin_xclk, conf.xclk_freq_hz); - ESP_LOGCONFIG(TAG, " I2C Pins: SDA:%d SCL:%d", conf.pin_sccb_sda, conf.pin_sccb_scl); - ESP_LOGCONFIG(TAG, " Reset Pin: %d", conf.pin_reset); + ESP_LOGCONFIG(TAG, + "ESP32 Camera:\n" + " Name: %s\n" + " Internal: %s\n" + " Data Pins: D0:%d D1:%d D2:%d D3:%d D4:%d D5:%d D6:%d D7:%d\n" + " VSYNC Pin: %d\n" + " HREF Pin: %d\n" + " Pixel Clock Pin: %d\n" + " External Clock: Pin:%d Frequency:%u\n" + " I2C Pins: SDA:%d SCL:%d\n" + " Reset Pin: %d", + this->name_.c_str(), YESNO(this->is_internal()), conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3, + conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7, conf.pin_vsync, conf.pin_href, conf.pin_pclk, + conf.pin_xclk, conf.xclk_freq_hz, conf.pin_sccb_sda, conf.pin_sccb_scl, conf.pin_reset); switch (this->config_.frame_size) { case FRAMESIZE_QQVGA: ESP_LOGCONFIG(TAG, " Resolution: 160x120 (QQVGA)"); @@ -123,24 +126,29 @@ void ESP32Camera::dump_config() { sensor_t *s = esp_camera_sensor_get(); auto st = s->status; - ESP_LOGCONFIG(TAG, " JPEG Quality: %u", st.quality); - ESP_LOGCONFIG(TAG, " Framebuffer Count: %u", conf.fb_count); - ESP_LOGCONFIG(TAG, " Contrast: %d", st.contrast); - ESP_LOGCONFIG(TAG, " Brightness: %d", st.brightness); - ESP_LOGCONFIG(TAG, " Saturation: %d", st.saturation); - ESP_LOGCONFIG(TAG, " Vertical Flip: %s", ONOFF(st.vflip)); - ESP_LOGCONFIG(TAG, " Horizontal Mirror: %s", ONOFF(st.hmirror)); - ESP_LOGCONFIG(TAG, " Special Effect: %u", st.special_effect); - ESP_LOGCONFIG(TAG, " White Balance Mode: %u", st.wb_mode); + ESP_LOGCONFIG(TAG, + " JPEG Quality: %u\n" + " Framebuffer Count: %u\n" + " Contrast: %d\n" + " Brightness: %d\n" + " Saturation: %d\n" + " Vertical Flip: %s\n" + " Horizontal Mirror: %s\n" + " Special Effect: %u\n" + " White Balance Mode: %u", + st.quality, conf.fb_count, st.contrast, st.brightness, st.saturation, ONOFF(st.vflip), + ONOFF(st.hmirror), st.special_effect, st.wb_mode); // ESP_LOGCONFIG(TAG, " Auto White Balance: %u", st.awb); // ESP_LOGCONFIG(TAG, " Auto White Balance Gain: %u", st.awb_gain); - ESP_LOGCONFIG(TAG, " Auto Exposure Control: %u", st.aec); - ESP_LOGCONFIG(TAG, " Auto Exposure Control 2: %u", st.aec2); - ESP_LOGCONFIG(TAG, " Auto Exposure Level: %d", st.ae_level); - ESP_LOGCONFIG(TAG, " Auto Exposure Value: %u", st.aec_value); - ESP_LOGCONFIG(TAG, " AGC: %u", st.agc); - ESP_LOGCONFIG(TAG, " AGC Gain: %u", st.agc_gain); - ESP_LOGCONFIG(TAG, " Gain Ceiling: %u", st.gainceiling); + ESP_LOGCONFIG(TAG, + " Auto Exposure Control: %u\n" + " Auto Exposure Control 2: %u\n" + " Auto Exposure Level: %d\n" + " Auto Exposure Value: %u\n" + " AGC: %u\n" + " AGC Gain: %u\n" + " Gain Ceiling: %u", + st.aec, st.aec2, st.ae_level, st.aec_value, st.agc, st.agc_gain, st.gainceiling); // ESP_LOGCONFIG(TAG, " BPC: %u", st.bpc); // ESP_LOGCONFIG(TAG, " WPC: %u", st.wpc); // ESP_LOGCONFIG(TAG, " RAW_GMA: %u", st.raw_gma); diff --git a/esphome/components/esp32_camera_web_server/camera_web_server.cpp b/esphome/components/esp32_camera_web_server/camera_web_server.cpp index 7ca0c56d23..0a83128908 100644 --- a/esphome/components/esp32_camera_web_server/camera_web_server.cpp +++ b/esphome/components/esp32_camera_web_server/camera_web_server.cpp @@ -85,8 +85,10 @@ void CameraWebServer::on_shutdown() { } void CameraWebServer::dump_config() { - ESP_LOGCONFIG(TAG, "ESP32 Camera Web Server:"); - ESP_LOGCONFIG(TAG, " Port: %d", this->port_); + ESP_LOGCONFIG(TAG, + "ESP32 Camera Web Server:\n" + " Port: %d", + this->port_); if (this->mode_ == STREAM) { ESP_LOGCONFIG(TAG, " Mode: stream"); } else { diff --git a/esphome/components/esp32_dac/esp32_dac.cpp b/esphome/components/esp32_dac/esp32_dac.cpp index 6f1577b8b1..01bf0e04c3 100644 --- a/esphome/components/esp32_dac/esp32_dac.cpp +++ b/esphome/components/esp32_dac/esp32_dac.cpp @@ -1,6 +1,6 @@ #include "esp32_dac.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_ESP32 @@ -20,7 +20,7 @@ static constexpr uint8_t DAC0_PIN = 25; static const char *const TAG = "esp32_dac"; void ESP32DAC::setup() { - ESP_LOGCONFIG(TAG, "Setting up ESP32 DAC Output..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->pin_->setup(); this->turn_off(); diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index ca39c1cd36..fa33bd947a 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -1,6 +1,7 @@ from esphome import automation import esphome.codegen as cg -from esphome.components import binary_sensor, output +from esphome.components import binary_sensor, esp32_ble, output +from esphome.components.esp32_ble import BTLoggers import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_ON_STATE, CONF_TRIGGER_ID @@ -94,6 +95,9 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.SMP) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index d74714838f..9d84d38968 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -324,10 +324,10 @@ void ESP32ImprovComponent::process_incoming_data_() { this->incoming_data_.clear(); } } else if (this->incoming_data_.size() - 2 > length) { - ESP_LOGV(TAG, "Too much data received or data malformed; resetting buffer..."); + ESP_LOGV(TAG, "Too much data received or data malformed; resetting buffer"); this->incoming_data_.clear(); } else { - ESP_LOGV(TAG, "Waiting for split data packets..."); + ESP_LOGV(TAG, "Waiting for split data packets"); } } diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.cpp b/esphome/components/esp32_rmt_led_strip/led_strip.cpp index 355f60ef05..88ddf24d49 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.cpp +++ b/esphome/components/esp32_rmt_led_strip/led_strip.cpp @@ -22,7 +22,7 @@ static const uint8_t RMT_CLK_DIV = 2; #endif void ESP32RMTLEDStripLightOutput::setup() { - ESP_LOGCONFIG(TAG, "Setting up ESP32 LED Strip..."); + ESP_LOGCONFIG(TAG, "Running setup"); size_t buffer_size = this->get_buffer_size_(); @@ -143,7 +143,7 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { this->last_refresh_ = now; this->mark_shown_(); - ESP_LOGVV(TAG, "Writing RGB values to bus..."); + ESP_LOGVV(TAG, "Writing RGB values to bus"); #if ESP_IDF_VERSION_MAJOR >= 5 esp_err_t error = rmt_tx_wait_all_done(this->channel_, 1000); @@ -247,8 +247,10 @@ light::ESPColorView ESP32RMTLEDStripLightOutput::get_view_internal(int32_t index } void ESP32RMTLEDStripLightOutput::dump_config() { - ESP_LOGCONFIG(TAG, "ESP32 RMT LED Strip:"); - ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); + ESP_LOGCONFIG(TAG, + "ESP32 RMT LED Strip:\n" + " Pin: %u", + this->pin_); #if ESP_IDF_VERSION_MAJOR >= 5 ESP_LOGCONFIG(TAG, " RMT Symbols: %" PRIu32, this->rmt_symbols_); #else @@ -278,9 +280,11 @@ void ESP32RMTLEDStripLightOutput::dump_config() { rgb_order = "UNKNOWN"; break; } - ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order); - ESP_LOGCONFIG(TAG, " Max refresh rate: %" PRIu32, *this->max_refresh_rate_); - ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_); + ESP_LOGCONFIG(TAG, + " RGB Order: %s\n" + " Max refresh rate: %" PRIu32 "\n" + " Number of LEDs: %u", + rgb_order, *this->max_refresh_rate_, this->num_leds_); } float ESP32RMTLEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; } diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index ae92d99b12..596770b96d 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -131,7 +131,9 @@ CONFIG_SCHEMA = cv.All( esp32_idf=192, esp32_s2_idf=192, esp32_s3_idf=192, + esp32_p4_idf=192, esp32_c3_idf=96, + esp32_c5_idf=96, esp32_c6_idf=96, esp32_h2_idf=96, ): cv.All(only_with_new_rmt_driver, cv.int_range(min=2)), @@ -140,7 +142,9 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_IS_RGBW, default=False): cv.boolean, cv.Optional(CONF_IS_WRGB, default=False): cv.boolean, cv.Optional(CONF_USE_DMA): cv.All( - esp32.only_on_variant(supported=[esp32.const.VARIANT_ESP32S3]), + esp32.only_on_variant( + supported=[esp32.const.VARIANT_ESP32S3, esp32.const.VARIANT_ESP32P4] + ), cv.only_with_esp_idf, cv.boolean, ), diff --git a/esphome/components/esp32_touch/esp32_touch.cpp b/esphome/components/esp32_touch/esp32_touch.cpp index 1dcb39e5de..366aa10697 100644 --- a/esphome/components/esp32_touch/esp32_touch.cpp +++ b/esphome/components/esp32_touch/esp32_touch.cpp @@ -13,7 +13,7 @@ namespace esp32_touch { static const char *const TAG = "esp32_touch"; void ESP32TouchComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up ESP32 Touch Hub..."); + ESP_LOGCONFIG(TAG, "Running setup"); touch_pad_init(); // set up and enable/start filtering based on ESP32 variant #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) @@ -75,9 +75,11 @@ void ESP32TouchComponent::setup() { } void ESP32TouchComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Config for ESP32 Touch Hub:"); - ESP_LOGCONFIG(TAG, " Meas cycle: %.2fms", this->meas_cycle_ / (8000000.0f / 1000.0f)); - ESP_LOGCONFIG(TAG, " Sleep cycle: %.2fms", this->sleep_cycle_ / (150000.0f / 1000.0f)); + ESP_LOGCONFIG(TAG, + "Config for ESP32 Touch Hub:\n" + " Meas cycle: %.2fms\n" + " Sleep cycle: %.2fms", + this->meas_cycle_ / (8000000.0f / 1000.0f), this->sleep_cycle_ / (150000.0f / 1000.0f)); const char *lv_s; switch (this->low_voltage_reference_) { @@ -171,10 +173,12 @@ void ESP32TouchComponent::dump_config() { filter_mode_s = "UNKNOWN"; break; } - ESP_LOGCONFIG(TAG, " Filter mode: %s", filter_mode_s); - ESP_LOGCONFIG(TAG, " Debounce count: %" PRIu32, this->debounce_count_); - ESP_LOGCONFIG(TAG, " Noise threshold coefficient: %" PRIu32, this->noise_threshold_); - ESP_LOGCONFIG(TAG, " Jitter filter step size: %" PRIu32, this->jitter_step_); + ESP_LOGCONFIG(TAG, + " Filter mode: %s\n" + " Debounce count: %" PRIu32 "\n" + " Noise threshold coefficient: %" PRIu32 "\n" + " Jitter filter step size: %" PRIu32, + filter_mode_s, this->debounce_count_, this->noise_threshold_, this->jitter_step_); const char *smooth_level_s; switch (this->smooth_level_) { case TOUCH_PAD_SMOOTH_OFF: diff --git a/esphome/components/esp8266/preferences.cpp b/esphome/components/esp8266/preferences.cpp index 8ee5a8225a..efd226e8f8 100644 --- a/esphome/components/esp8266/preferences.cpp +++ b/esphome/components/esp8266/preferences.cpp @@ -169,7 +169,7 @@ class ESP8266Preferences : public ESPPreferences { void setup() { s_flash_storage = new uint32_t[ESP8266_FLASH_STORAGE_SIZE]; // NOLINT - ESP_LOGVV(TAG, "Loading preferences from flash..."); + ESP_LOGVV(TAG, "Loading preferences from flash"); { InterruptLock lock; @@ -235,7 +235,7 @@ class ESP8266Preferences : public ESPPreferences { if (s_prevent_write) return false; - ESP_LOGD(TAG, "Saving preferences to flash..."); + ESP_LOGD(TAG, "Saving"); SpiFlashOpResult erase_res, write_res = SPI_FLASH_RESULT_OK; { InterruptLock lock; @@ -245,11 +245,11 @@ class ESP8266Preferences : public ESPPreferences { } } if (erase_res != SPI_FLASH_RESULT_OK) { - ESP_LOGE(TAG, "Erase ESP8266 flash failed!"); + ESP_LOGE(TAG, "Erasing failed"); return false; } if (write_res != SPI_FLASH_RESULT_OK) { - ESP_LOGE(TAG, "Write ESP8266 flash failed!"); + ESP_LOGE(TAG, "Writing failed"); return false; } @@ -258,14 +258,14 @@ class ESP8266Preferences : public ESPPreferences { } bool reset() override { - ESP_LOGD(TAG, "Cleaning up preferences in flash..."); + ESP_LOGD(TAG, "Erasing storage"); SpiFlashOpResult erase_res; { InterruptLock lock; erase_res = spi_flash_erase_sector(get_esp8266_flash_sector()); } if (erase_res != SPI_FLASH_RESULT_OK) { - ESP_LOGE(TAG, "Erase ESP8266 flash failed!"); + ESP_LOGE(TAG, "Erasing failed"); return false; } diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.cpp b/esphome/components/esp8266_pwm/esp8266_pwm.cpp index 8b3d8613b0..03fa3c683e 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.cpp +++ b/esphome/components/esp8266_pwm/esp8266_pwm.cpp @@ -1,10 +1,10 @@ #ifdef USE_ESP8266 #include "esp8266_pwm.h" -#include "esphome/core/macros.h" #include "esphome/core/defines.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include "esphome/core/macros.h" #include @@ -14,7 +14,7 @@ namespace esp8266_pwm { static const char *const TAG = "esp8266_pwm"; void ESP8266PWM::setup() { - ESP_LOGCONFIG(TAG, "Setting up ESP8266 PWM Output..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->pin_->setup(); this->turn_off(); } diff --git a/esphome/components/esp_ldo/__init__.py b/esphome/components/esp_ldo/__init__.py new file mode 100644 index 0000000000..ce24028083 --- /dev/null +++ b/esphome/components/esp_ldo/__init__.py @@ -0,0 +1,91 @@ +from esphome.automation import Action, register_action +import esphome.codegen as cg +from esphome.components.esp32 import VARIANT_ESP32P4, only_on_variant +import esphome.config_validation as cv +from esphome.const import CONF_CHANNEL, CONF_ID, CONF_VOLTAGE +from esphome.final_validate import full_config + +CODEOWNERS = ["@clydebarrow"] + +DOMAIN = "esp_ldo" + +esp_ldo_ns = cg.esphome_ns.namespace("esp_ldo") +EspLdo = esp_ldo_ns.class_("EspLdo", cg.Component) +AdjustAction = esp_ldo_ns.class_("AdjustAction", Action) + +CHANNELS = (3, 4) +CONF_ADJUSTABLE = "adjustable" + +adjusted_ids = set() + +CONFIG_SCHEMA = cv.All( + cv.ensure_list( + { + cv.GenerateID(): cv.declare_id(EspLdo), + cv.Required(CONF_VOLTAGE): cv.All( + cv.voltage, cv.float_range(min=0.5, max=2.7) + ), + cv.Required(CONF_CHANNEL): cv.one_of(*CHANNELS, int=True), + cv.Optional(CONF_ADJUSTABLE, default=False): cv.boolean, + } + ), + cv.only_with_esp_idf, + only_on_variant(supported=[VARIANT_ESP32P4]), +) + + +async def to_code(configs): + for config in configs: + var = cg.new_Pvariable(config[CONF_ID], config[CONF_CHANNEL]) + await cg.register_component(var, config) + cg.add(var.set_voltage(config[CONF_VOLTAGE])) + cg.add(var.set_adjustable(config[CONF_ADJUSTABLE])) + + +def final_validate(configs): + for channel in CHANNELS: + used = [config for config in configs if config[CONF_CHANNEL] == channel] + if len(used) > 1: + raise cv.Invalid( + f"Multiple LDOs configured for channel {channel}. Each channel must be unique.", + path=[CONF_CHANNEL, channel], + ) + + global_config = full_config.get() + for w in adjusted_ids: + path = global_config.get_path_for_id(w) + ldo_conf = global_config.get_config_for_path(path[:-1]) + if not ldo_conf[CONF_ADJUSTABLE]: + raise cv.Invalid( + "A non adjustable LDO may not be adjusted.", + path, + ) + + +FINAL_VALIDATE_SCHEMA = final_validate + + +def adjusted_ldo_id(value): + value = cv.use_id(EspLdo)(value) + adjusted_ids.add(value) + return value + + +@register_action( + "esp_ldo.voltage.adjust", + AdjustAction, + cv.Schema( + { + cv.GenerateID(CONF_ID): adjusted_ldo_id, + cv.Required(CONF_VOLTAGE): cv.templatable( + cv.All(cv.voltage, cv.float_range(min=0.5, max=2.7)) + ), + } + ), +) +async def ldo_voltage_adjust_to_code(config, action_id, template_arg, args): + parent = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, parent) + template_ = await cg.templatable(config[CONF_VOLTAGE], args, cg.float_) + cg.add(var.set_voltage(template_)) + return var diff --git a/esphome/components/esp_ldo/esp_ldo.cpp b/esphome/components/esp_ldo/esp_ldo.cpp new file mode 100644 index 0000000000..eb04670d7e --- /dev/null +++ b/esphome/components/esp_ldo/esp_ldo.cpp @@ -0,0 +1,43 @@ +#ifdef USE_ESP32_VARIANT_ESP32P4 +#include "esp_ldo.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace esp_ldo { + +static const char *const TAG = "esp_ldo"; +void EspLdo::setup() { + esp_ldo_channel_config_t config{}; + config.chan_id = this->channel_; + config.voltage_mv = (int) (this->voltage_ * 1000.0f); + config.flags.adjustable = this->adjustable_; + auto err = esp_ldo_acquire_channel(&config, &this->handle_); + if (err != ESP_OK) { + auto msg = str_sprintf("Failed to acquire LDO channel %d with voltage %fV", this->channel_, this->voltage_); + this->mark_failed(msg.c_str()); + } else { + ESP_LOGD(TAG, "Acquired LDO channel %d with voltage %fV", this->channel_, this->voltage_); + } +} +void EspLdo::dump_config() { + ESP_LOGCONFIG(TAG, "ESP LDO Channel %d:", this->channel_); + ESP_LOGCONFIG(TAG, " Voltage: %fV", this->voltage_); + ESP_LOGCONFIG(TAG, " Adjustable: %s", YESNO(this->adjustable_)); +} + +void EspLdo::adjust_voltage(float voltage) { + if (!std::isfinite(voltage) || voltage < 0.5f || voltage > 2.7f) { + ESP_LOGE(TAG, "Invalid voltage %fV for LDO channel %d", voltage, this->channel_); + return; + } + auto erro = esp_ldo_channel_adjust_voltage(this->handle_, (int) (voltage * 1000.0f)); + if (erro != ESP_OK) { + ESP_LOGE(TAG, "Failed to adjust LDO channel %d to voltage %fV: %s", this->channel_, voltage, esp_err_to_name(erro)); + } +} + +} // namespace esp_ldo +} // namespace esphome + +#endif // USE_ESP32_VARIANT_ESP32P4 diff --git a/esphome/components/esp_ldo/esp_ldo.h b/esphome/components/esp_ldo/esp_ldo.h new file mode 100644 index 0000000000..fb5abf7a3a --- /dev/null +++ b/esphome/components/esp_ldo/esp_ldo.h @@ -0,0 +1,43 @@ +#pragma once +#ifdef USE_ESP32_VARIANT_ESP32P4 +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esp_ldo_regulator.h" + +namespace esphome { +namespace esp_ldo { + +class EspLdo : public Component { + public: + EspLdo(int channel) : channel_(channel) {} + + void setup() override; + void dump_config() override; + + void set_adjustable(bool adjustable) { this->adjustable_ = adjustable; } + void set_voltage(float voltage) { this->voltage_ = voltage; } + void adjust_voltage(float voltage); + + protected: + int channel_; + float voltage_{2.7}; + bool adjustable_{false}; + esp_ldo_channel_handle_t handle_{}; +}; + +template class AdjustAction : public Action { + public: + explicit AdjustAction(EspLdo *ldo) : ldo_(ldo) {} + + TEMPLATABLE_VALUE(float, voltage) + + void play(Ts... x) override { this->ldo_->adjust_voltage(this->voltage_.value(x...)); } + + protected: + EspLdo *ldo_; +}; + +} // namespace esp_ldo +} // namespace esphome + +#endif // USE_ESP32_VARIANT_ESP32P4 diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index 7e2ef42a97..227cb676ff 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -26,7 +26,7 @@ void ESPHomeOTAComponent::setup() { ota::register_ota_platform(this); #endif - server_ = socket::socket_ip(SOCK_STREAM, 0); + server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections if (server_ == nullptr) { ESP_LOGW(TAG, "Could not create socket"); this->mark_failed(); @@ -70,9 +70,11 @@ void ESPHomeOTAComponent::setup() { } void ESPHomeOTAComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Over-The-Air updates:"); - ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); - ESP_LOGCONFIG(TAG, " Version: %d", USE_OTA_VERSION); + ESP_LOGCONFIG(TAG, + "Over-The-Air updates:\n" + " Address: %s:%u\n" + " Version: %d", + network::get_use_address().c_str(), this->port_, USE_OTA_VERSION); #ifdef USE_OTA_PASSWORD if (!this->password_.empty()) { ESP_LOGCONFIG(TAG, " Password configured"); @@ -100,9 +102,12 @@ void ESPHomeOTAComponent::handle_() { #endif if (client_ == nullptr) { - struct sockaddr_storage source_addr; - socklen_t addr_len = sizeof(source_addr); - client_ = server_->accept((struct sockaddr *) &source_addr, &addr_len); + // Check if the server socket is ready before accepting + if (this->server_->ready()) { + struct sockaddr_storage source_addr; + socklen_t addr_len = sizeof(source_addr); + client_ = server_->accept((struct sockaddr *) &source_addr, &addr_len); + } } if (client_ == nullptr) return; @@ -111,10 +116,12 @@ void ESPHomeOTAComponent::handle_() { int err = client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int)); if (err != 0) { ESP_LOGW(TAG, "Socket could not enable TCP nodelay, errno %d", errno); + client_->close(); + client_ = nullptr; return; } - ESP_LOGD(TAG, "Starting update from %s...", this->client_->getpeername().c_str()); + ESP_LOGD(TAG, "Starting update from %s", this->client_->getpeername().c_str()); this->status_set_warning(); #ifdef USE_OTA_STATE_CALLBACK this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0); diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 79ef0715d8..fe96973924 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -38,7 +38,7 @@ EthernetComponent *global_eth_component; // NOLINT(cppcoreguidelines-avoid-non- EthernetComponent::EthernetComponent() { global_eth_component = this; } void EthernetComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up Ethernet..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (esp_reset_reason() != ESP_RST_DEEPSLEEP) { // Delay here to allow power to stabilise before Ethernet is initialized. delay(300); // NOLINT @@ -245,33 +245,33 @@ void EthernetComponent::loop() { switch (this->state_) { case EthernetComponentState::STOPPED: if (this->started_) { - ESP_LOGI(TAG, "Starting ethernet connection"); + ESP_LOGI(TAG, "Starting connection"); this->state_ = EthernetComponentState::CONNECTING; this->start_connect_(); } break; case EthernetComponentState::CONNECTING: if (!this->started_) { - ESP_LOGI(TAG, "Stopped ethernet connection"); + ESP_LOGI(TAG, "Stopped connection"); this->state_ = EthernetComponentState::STOPPED; } else if (this->connected_) { // connection established - ESP_LOGI(TAG, "Connected via Ethernet!"); + ESP_LOGI(TAG, "Connected"); this->state_ = EthernetComponentState::CONNECTED; this->dump_connect_params_(); this->status_clear_warning(); } else if (now - this->connect_begin_ > 15000) { - ESP_LOGW(TAG, "Connecting via ethernet failed! Re-connecting..."); + ESP_LOGW(TAG, "Connecting failed; reconnecting"); this->start_connect_(); } break; case EthernetComponentState::CONNECTED: if (!this->started_) { - ESP_LOGI(TAG, "Stopped ethernet connection"); + ESP_LOGI(TAG, "Stopped connection"); this->state_ = EthernetComponentState::STOPPED; } else if (!this->connected_) { - ESP_LOGW(TAG, "Connection via Ethernet lost! Re-connecting..."); + ESP_LOGW(TAG, "Connection lost; reconnecting"); this->state_ = EthernetComponentState::CONNECTING; this->start_connect_(); } @@ -326,10 +326,12 @@ void EthernetComponent::dump_config() { ESP_LOGCONFIG(TAG, "Ethernet:"); this->dump_connect_params_(); #ifdef USE_ETHERNET_SPI - ESP_LOGCONFIG(TAG, " CLK Pin: %u", this->clk_pin_); - ESP_LOGCONFIG(TAG, " MISO Pin: %u", this->miso_pin_); - ESP_LOGCONFIG(TAG, " MOSI Pin: %u", this->mosi_pin_); - ESP_LOGCONFIG(TAG, " CS Pin: %u", this->cs_pin_); + ESP_LOGCONFIG(TAG, + " CLK Pin: %u\n" + " MISO Pin: %u\n" + " MOSI Pin: %u\n" + " CS Pin: %u", + this->clk_pin_, this->miso_pin_, this->mosi_pin_, this->cs_pin_); #ifdef USE_ETHERNET_SPI_POLLING_SUPPORT if (this->polling_interval_ != 0) { ESP_LOGCONFIG(TAG, " Polling Interval: %lu ms", this->polling_interval_); @@ -338,15 +340,19 @@ void EthernetComponent::dump_config() { { ESP_LOGCONFIG(TAG, " IRQ Pin: %d", this->interrupt_pin_); } - ESP_LOGCONFIG(TAG, " Reset Pin: %d", this->reset_pin_); - ESP_LOGCONFIG(TAG, " Clock Speed: %d MHz", this->clock_speed_ / 1000000); + ESP_LOGCONFIG(TAG, + " Reset Pin: %d\n" + " Clock Speed: %d MHz", + this->reset_pin_, this->clock_speed_ / 1000000); #else if (this->power_pin_ != -1) { ESP_LOGCONFIG(TAG, " Power Pin: %u", this->power_pin_); } - ESP_LOGCONFIG(TAG, " MDC Pin: %u", this->mdc_pin_); - ESP_LOGCONFIG(TAG, " MDIO Pin: %u", this->mdio_pin_); - ESP_LOGCONFIG(TAG, " PHY addr: %u", this->phy_addr_); + ESP_LOGCONFIG(TAG, + " MDC Pin: %u\n" + " MDIO Pin: %u\n" + " PHY addr: %u", + this->mdc_pin_, this->mdio_pin_, this->phy_addr_); #endif ESP_LOGCONFIG(TAG, " Type: %s", eth_type); } @@ -512,16 +518,19 @@ bool EthernetComponent::is_connected() { return this->state_ == EthernetComponen void EthernetComponent::dump_connect_params_() { esp_netif_ip_info_t ip; esp_netif_get_ip_info(this->eth_netif_, &ip); - ESP_LOGCONFIG(TAG, " IP Address: %s", network::IPAddress(&ip.ip).str().c_str()); - ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); - ESP_LOGCONFIG(TAG, " Subnet: %s", network::IPAddress(&ip.netmask).str().c_str()); - ESP_LOGCONFIG(TAG, " Gateway: %s", network::IPAddress(&ip.gw).str().c_str()); - const ip_addr_t *dns_ip1 = dns_getserver(0); const ip_addr_t *dns_ip2 = dns_getserver(1); - ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1).str().c_str()); - ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2).str().c_str()); + ESP_LOGCONFIG(TAG, + " IP Address: %s\n" + " Hostname: '%s'\n" + " Subnet: %s\n" + " Gateway: %s\n" + " DNS1: %s\n" + " DNS2: %s", + network::IPAddress(&ip.ip).str().c_str(), App.get_name().c_str(), + network::IPAddress(&ip.netmask).str().c_str(), network::IPAddress(&ip.gw).str().c_str(), + network::IPAddress(dns_ip1).str().c_str(), network::IPAddress(dns_ip2).str().c_str()); #if USE_NETWORK_IPV6 struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES]; @@ -533,9 +542,12 @@ void EthernetComponent::dump_connect_params_() { } #endif /* USE_NETWORK_IPV6 */ - ESP_LOGCONFIG(TAG, " MAC Address: %s", this->get_eth_mac_address_pretty().c_str()); - ESP_LOGCONFIG(TAG, " Is Full Duplex: %s", YESNO(this->get_duplex_mode() == ETH_DUPLEX_FULL)); - ESP_LOGCONFIG(TAG, " Link Speed: %u", this->get_link_speed() == ETH_SPEED_100M ? 100 : 10); + ESP_LOGCONFIG(TAG, + " MAC Address: %s\n" + " Is Full Duplex: %s\n" + " Link Speed: %u", + this->get_eth_mac_address_pretty().c_str(), YESNO(this->get_duplex_mode() == ETH_DUPLEX_FULL), + this->get_link_speed() == ETH_SPEED_100M ? 100 : 10); } #ifdef USE_ETHERNET_SPI diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index fb178431d5..7a205d89f0 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -56,7 +56,7 @@ class EthernetComponent : public Component { void dump_config() override; float get_setup_priority() const override; bool can_proceed() override; - void on_shutdown() override { powerdown(); } + void on_powerdown() override { powerdown(); } bool is_connected(); #ifdef USE_ETHERNET_SPI diff --git a/esphome/components/event/__init__.py b/esphome/components/event/__init__.py index 0e5fb43690..e7ab489a25 100644 --- a/esphome/components/event/__init__.py +++ b/esphome/components/event/__init__.py @@ -113,6 +113,7 @@ async def register_event(var, config, *, event_types: list[str]): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_event(var)) + CORE.register_platform_component("event", var) await setup_event_core_(var, config, event_types=event_types) diff --git a/esphome/components/exposure_notifications/exposure_notifications.cpp b/esphome/components/exposure_notifications/exposure_notifications.cpp index 3083cf429c..307bee26f8 100644 --- a/esphome/components/exposure_notifications/exposure_notifications.cpp +++ b/esphome/components/exposure_notifications/exposure_notifications.cpp @@ -1,6 +1,6 @@ #include "exposure_notifications.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_ESP32 diff --git a/esphome/components/ezo/ezo.cpp b/esphome/components/ezo/ezo.cpp index 10f3d530ce..2e92c58e29 100644 --- a/esphome/components/ezo/ezo.cpp +++ b/esphome/components/ezo/ezo.cpp @@ -15,7 +15,7 @@ void EZOSensor::dump_config() { LOG_SENSOR("", "EZO", this); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with EZO circuit failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/ezo_pmp/ezo_pmp.cpp b/esphome/components/ezo_pmp/ezo_pmp.cpp index 6e5779a12e..9ec41cce30 100644 --- a/esphome/components/ezo_pmp/ezo_pmp.cpp +++ b/esphome/components/ezo_pmp/ezo_pmp.cpp @@ -41,7 +41,7 @@ static const std::string DOSING_MODE_CONTINUOUS = "Continuous"; void EzoPMP::dump_config() { LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with EZO-PMP circuit failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/factory_reset/button/factory_reset_button.cpp b/esphome/components/factory_reset/button/factory_reset_button.cpp index 9354a3363e..585975c043 100644 --- a/esphome/components/factory_reset/button/factory_reset_button.cpp +++ b/esphome/components/factory_reset/button/factory_reset_button.cpp @@ -10,7 +10,7 @@ static const char *const TAG = "factory_reset.button"; void FactoryResetButton::dump_config() { LOG_BUTTON("", "Factory Reset Button", this); } void FactoryResetButton::press_action() { - ESP_LOGI(TAG, "Resetting to factory defaults..."); + ESP_LOGI(TAG, "Resetting"); // Let MQTT settle a bit delay(100); // NOLINT global_preferences->reset(); diff --git a/esphome/components/factory_reset/switch/factory_reset_switch.cpp b/esphome/components/factory_reset/switch/factory_reset_switch.cpp index 7bc8676736..1282c73f4e 100644 --- a/esphome/components/factory_reset/switch/factory_reset_switch.cpp +++ b/esphome/components/factory_reset/switch/factory_reset_switch.cpp @@ -14,7 +14,7 @@ void FactoryResetSwitch::write_state(bool state) { this->publish_state(false); if (state) { - ESP_LOGI(TAG, "Resetting to factory defaults..."); + ESP_LOGI(TAG, "Resetting"); // Let MQTT settle a bit delay(100); // NOLINT global_preferences->reset(); diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 960809ff70..c6ff938cd6 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -296,6 +296,7 @@ async def register_fan(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_fan(var)) + CORE.register_platform_component("fan", var) await setup_fan_core_(var, config) diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp index 1d560d2fc6..25f710f893 100644 --- a/esphome/components/fan/fan.cpp +++ b/esphome/components/fan/fan.cpp @@ -41,39 +41,48 @@ void FanCall::perform() { void FanCall::validate_() { auto traits = this->parent_.get_traits(); - if (this->speed_.has_value()) + if (this->speed_.has_value()) { this->speed_ = clamp(*this->speed_, 1, traits.supported_speed_count()); - if (this->binary_state_.has_value() && *this->binary_state_) { - // when turning on, if neither current nor new speed available, set speed to 100% - if (traits.supports_speed() && !this->parent_.state && this->parent_.speed == 0 && !this->speed_.has_value()) { - this->speed_ = traits.supported_speed_count(); - } - } - - if (this->oscillating_.has_value() && !traits.supports_oscillation()) { - ESP_LOGW(TAG, "'%s' - This fan does not support oscillation!", this->parent_.get_name().c_str()); - this->oscillating_.reset(); - } - - if (this->speed_.has_value() && !traits.supports_speed()) { - ESP_LOGW(TAG, "'%s' - This fan does not support speeds!", this->parent_.get_name().c_str()); - this->speed_.reset(); - } - - if (this->direction_.has_value() && !traits.supports_direction()) { - ESP_LOGW(TAG, "'%s' - This fan does not support directions!", this->parent_.get_name().c_str()); - this->direction_.reset(); + // https://developers.home-assistant.io/docs/core/entity/fan/#preset-modes + // "Manually setting a speed must disable any set preset mode" + this->preset_mode_.clear(); } if (!this->preset_mode_.empty()) { const auto &preset_modes = traits.supported_preset_modes(); if (preset_modes.find(this->preset_mode_) == preset_modes.end()) { - ESP_LOGW(TAG, "'%s' - This fan does not support preset mode '%s'!", this->parent_.get_name().c_str(), - this->preset_mode_.c_str()); + ESP_LOGW(TAG, "%s: Preset mode '%s' not supported", this->parent_.get_name().c_str(), this->preset_mode_.c_str()); this->preset_mode_.clear(); } } + + // when turning on... + if (!this->parent_.state && this->binary_state_.has_value() && + *this->binary_state_ + // ..,and no preset mode will be active... + && this->preset_mode_.empty() && + this->parent_.preset_mode.empty() + // ...and neither current nor new speed is available... + && traits.supports_speed() && this->parent_.speed == 0 && !this->speed_.has_value()) { + // ...set speed to 100% + this->speed_ = traits.supported_speed_count(); + } + + if (this->oscillating_.has_value() && !traits.supports_oscillation()) { + ESP_LOGW(TAG, "%s: Oscillation not supported", this->parent_.get_name().c_str()); + this->oscillating_.reset(); + } + + if (this->speed_.has_value() && !traits.supports_speed()) { + ESP_LOGW(TAG, "%s: Speed control not supported", this->parent_.get_name().c_str()); + this->speed_.reset(); + } + + if (this->direction_.has_value() && !traits.supports_direction()) { + ESP_LOGW(TAG, "%s: Direction control not supported", this->parent_.get_name().c_str()); + this->direction_.reset(); + } } FanCall FanRestoreState::to_call(Fan &fan) { @@ -189,8 +198,10 @@ void Fan::dump_traits_(const char *tag, const char *prefix) { auto traits = this->get_traits(); if (traits.supports_speed()) { - ESP_LOGCONFIG(tag, "%s Speed: YES", prefix); - ESP_LOGCONFIG(tag, "%s Speed count: %d", prefix, traits.supported_speed_count()); + ESP_LOGCONFIG(tag, + "%s Speed: YES\n" + "%s Speed count: %d", + prefix, prefix, traits.supported_speed_count()); } if (traits.supports_oscillation()) { ESP_LOGCONFIG(tag, "%s Oscillation: YES", prefix); diff --git a/esphome/components/fastled_base/fastled_light.cpp b/esphome/components/fastled_base/fastled_light.cpp index 3ecdee61b1..bca7de811a 100644 --- a/esphome/components/fastled_base/fastled_light.cpp +++ b/esphome/components/fastled_base/fastled_light.cpp @@ -9,7 +9,7 @@ namespace fastled_base { static const char *const TAG = "fastled"; void FastLEDLightOutput::setup() { - ESP_LOGCONFIG(TAG, "Setting up FastLED light..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->controller_->init(); this->controller_->setLeds(this->leds_, this->num_leds_); this->effect_data_ = new uint8_t[this->num_leds_]; // NOLINT @@ -18,9 +18,11 @@ void FastLEDLightOutput::setup() { } } void FastLEDLightOutput::dump_config() { - ESP_LOGCONFIG(TAG, "FastLED light:"); - ESP_LOGCONFIG(TAG, " Num LEDs: %u", this->num_leds_); - ESP_LOGCONFIG(TAG, " Max refresh rate: %u", *this->max_refresh_rate_); + ESP_LOGCONFIG(TAG, + "FastLED light:\n" + " Num LEDs: %u\n" + " Max refresh rate: %u", + this->num_leds_, *this->max_refresh_rate_); } void FastLEDLightOutput::write_state(light::LightState *state) { // protect from refreshing too often @@ -33,7 +35,7 @@ void FastLEDLightOutput::write_state(light::LightState *state) { this->last_refresh_ = now; this->mark_shown_(); - ESP_LOGVV(TAG, "Writing RGB values to bus..."); + ESP_LOGVV(TAG, "Writing RGB values to bus"); this->controller_->showLeds(this->state_parent_->current_values.get_brightness() * 255); } diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.cpp b/esphome/components/fingerprint_grow/fingerprint_grow.cpp index 0dfea49b8b..e28548428c 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.cpp +++ b/esphome/components/fingerprint_grow/fingerprint_grow.cpp @@ -57,7 +57,7 @@ void FingerprintGrowComponent::update() { } void FingerprintGrowComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up Grow Fingerprint Reader..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->has_sensing_pin_ = (this->sensing_pin_ != nullptr); this->has_power_pin_ = (this->sensor_power_pin_ != nullptr); @@ -534,11 +534,13 @@ void FingerprintGrowComponent::sensor_sleep_() { } void FingerprintGrowComponent::dump_config() { - ESP_LOGCONFIG(TAG, "GROW_FINGERPRINT_READER:"); - ESP_LOGCONFIG(TAG, " System Identifier Code: 0x%.4X", this->system_identifier_code_); - ESP_LOGCONFIG(TAG, " Touch Sensing Pin: %s", - this->has_sensing_pin_ ? this->sensing_pin_->dump_summary().c_str() : "None"); - ESP_LOGCONFIG(TAG, " Sensor Power Pin: %s", + ESP_LOGCONFIG(TAG, + "GROW_FINGERPRINT_READER:\n" + " System Identifier Code: 0x%.4X\n" + " Touch Sensing Pin: %s\n" + " Sensor Power Pin: %s", + this->system_identifier_code_, + this->has_sensing_pin_ ? this->sensing_pin_->dump_summary().c_str() : "None", this->has_power_pin_ ? this->sensor_power_pin_->dump_summary().c_str() : "None"); if (this->idle_period_to_sleep_ms_ < UINT32_MAX) { ESP_LOGCONFIG(TAG, " Idle Period to Sleep: %" PRIu32 " ms", this->idle_period_to_sleep_ms_); diff --git a/esphome/components/fs3000/fs3000.cpp b/esphome/components/fs3000/fs3000.cpp index fb729ed0a0..c99772a23d 100644 --- a/esphome/components/fs3000/fs3000.cpp +++ b/esphome/components/fs3000/fs3000.cpp @@ -7,7 +7,7 @@ namespace fs3000 { static const char *const TAG = "fs3000"; void FS3000Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up FS3000..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (model_ == FIVE) { // datasheet gives 9 points to interpolate from for the 1005 model diff --git a/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp b/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp index bd603fdc10..9873a88fde 100644 --- a/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp +++ b/esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp @@ -1,5 +1,6 @@ #include "ft5x06_touchscreen.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -8,7 +9,7 @@ namespace ft5x06 { static const char *const TAG = "ft5x06.touchscreen"; void FT5x06Touchscreen::setup() { - ESP_LOGCONFIG(TAG, "Setting up FT5x06 Touchscreen..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (this->interrupt_pin_ != nullptr) { this->interrupt_pin_->setup(); this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); @@ -81,9 +82,11 @@ void FT5x06Touchscreen::update_touches() { } void FT5x06Touchscreen::dump_config() { - ESP_LOGCONFIG(TAG, "FT5x06 Touchscreen:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); - ESP_LOGCONFIG(TAG, " Vendor ID: 0x%X", (int) this->vendor_id_); + ESP_LOGCONFIG(TAG, + "FT5x06 Touchscreen:\n" + " Address: 0x%02X\n" + " Vendor ID: 0x%X", + this->address_, (int) this->vendor_id_); } bool FT5x06Touchscreen::err_check_(i2c::ErrorCode err, const char *msg) { diff --git a/esphome/components/ft63x6/ft63x6.cpp b/esphome/components/ft63x6/ft63x6.cpp index 8bc66822eb..ba5b2094a5 100644 --- a/esphome/components/ft63x6/ft63x6.cpp +++ b/esphome/components/ft63x6/ft63x6.cpp @@ -28,7 +28,7 @@ static const uint8_t FT63X6_ADDR_CHIP_ID = 0xA3; static const char *const TAG = "FT63X6"; void FT63X6Touchscreen::setup() { - ESP_LOGCONFIG(TAG, "Setting up FT63X6 Touchscreen..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (this->interrupt_pin_ != nullptr) { this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); this->interrupt_pin_->setup(); @@ -71,8 +71,10 @@ void FT63X6Touchscreen::dump_config() { LOG_I2C_DEVICE(this); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " X Calibration: [%d, %d]", this->x_raw_min_, this->x_raw_max_); - ESP_LOGCONFIG(TAG, " Y Calibration: [%d, %d]", this->y_raw_min_, this->y_raw_max_); + ESP_LOGCONFIG(TAG, + " X Calibration: [%d, %d]\n" + " Y Calibration: [%d, %d]", + this->x_raw_min_, this->x_raw_max_, this->y_raw_min_, this->y_raw_max_); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/gcja5/gcja5.cpp b/esphome/components/gcja5/gcja5.cpp index 64b237515b..a7342bc828 100644 --- a/esphome/components/gcja5/gcja5.cpp +++ b/esphome/components/gcja5/gcja5.cpp @@ -14,8 +14,6 @@ namespace gcja5 { static const char *const TAG = "gcja5"; -void GCJA5Component::setup() { ESP_LOGCONFIG(TAG, "Setting up gcja5..."); } - void GCJA5Component::loop() { const uint32_t now = App.get_loop_component_start_time(); if (now - this->last_transmission_ >= 500) { @@ -78,16 +76,6 @@ bool GCJA5Component::calculate_checksum_() { return (crc == this->rx_message_[30]); } -uint32_t GCJA5Component::get_32_bit_uint_(uint8_t start_index) { - return (((uint32_t) this->rx_message_[start_index + 3]) << 24) | - (((uint32_t) this->rx_message_[start_index + 2]) << 16) | - (((uint32_t) this->rx_message_[start_index + 1]) << 8) | ((uint32_t) this->rx_message_[start_index]); -} - -uint16_t GCJA5Component::get_16_bit_uint_(uint8_t start_index) { - return (((uint32_t) this->rx_message_[start_index + 1]) << 8) | ((uint32_t) this->rx_message_[start_index]); -} - void GCJA5Component::parse_data_() { ESP_LOGVV(TAG, "GCJA5 Data: "); for (uint8_t i = 0; i < 32; i++) { diff --git a/esphome/components/gcja5/gcja5.h b/esphome/components/gcja5/gcja5.h index 7593c90323..ea1fb78bf0 100644 --- a/esphome/components/gcja5/gcja5.h +++ b/esphome/components/gcja5/gcja5.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/uart/uart.h" @@ -9,7 +10,6 @@ namespace gcja5 { class GCJA5Component : public Component, public uart::UARTDevice { public: - void setup() override; void dump_config() override; void loop() override; float get_setup_priority() const override { return setup_priority::DATA; } @@ -29,8 +29,13 @@ class GCJA5Component : public Component, public uart::UARTDevice { void parse_data_(); bool calculate_checksum_(); - uint32_t get_32_bit_uint_(uint8_t start_index); - uint16_t get_16_bit_uint_(uint8_t start_index); + uint16_t get_16_bit_uint_(uint8_t start_index) const { + return encode_uint16(this->rx_message_[start_index + 1], this->rx_message_[start_index]); + } + uint32_t get_32_bit_uint_(uint8_t start_index) const { + return encode_uint32(this->rx_message_[start_index + 3], this->rx_message_[start_index + 2], + this->rx_message_[start_index + 1], this->rx_message_[start_index]); + } uint32_t last_transmission_{0}; std::vector rx_message_; diff --git a/esphome/components/gdk101/gdk101.cpp b/esphome/components/gdk101/gdk101.cpp index 93f3c20fa8..e8401aa09b 100644 --- a/esphome/components/gdk101/gdk101.cpp +++ b/esphome/components/gdk101/gdk101.cpp @@ -34,7 +34,7 @@ void GDK101Component::update() { void GDK101Component::setup() { uint8_t data[2]; - ESP_LOGCONFIG(TAG, "Setting up GDK101..."); + ESP_LOGCONFIG(TAG, "Running setup"); // first, reset the sensor if (!this->reset_sensor_(data)) { this->status_set_error("Reset failed!"); @@ -60,7 +60,7 @@ void GDK101Component::dump_config() { ESP_LOGCONFIG(TAG, "GDK101:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with GDK101 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } #ifdef USE_SENSOR LOG_SENSOR(" ", "Firmware Version", this->fw_version_sensor_); diff --git a/esphome/components/globals/globals_component.h b/esphome/components/globals/globals_component.h index 78808436af..4c6a12aa72 100644 --- a/esphome/components/globals/globals_component.h +++ b/esphome/components/globals/globals_component.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/core/automation.h" +#include "esphome/core/component.h" #include "esphome/core/helpers.h" #include @@ -39,7 +39,7 @@ template class RestoringGlobalsComponent : public Component { void setup() override { this->rtc_ = global_preferences->make_preference(1944399030U ^ this->name_hash_); this->rtc_.load(&this->value_); - memcpy(&this->prev_value_, &this->value_, sizeof(T)); + memcpy(&this->last_checked_value_, &this->value_, sizeof(T)); } float get_setup_priority() const override { return setup_priority::HARDWARE; } @@ -52,15 +52,15 @@ template class RestoringGlobalsComponent : public Component { protected: void store_value_() { - int diff = memcmp(&this->value_, &this->prev_value_, sizeof(T)); + int diff = memcmp(&this->value_, &this->last_checked_value_, sizeof(T)); if (diff != 0) { this->rtc_.save(&this->value_); - memcpy(&this->prev_value_, &this->value_, sizeof(T)); + memcpy(&this->last_checked_value_, &this->value_, sizeof(T)); } } T value_{}; - T prev_value_{}; + T last_checked_value_{}; uint32_t name_hash_{}; ESPPreferenceObject rtc_; }; @@ -85,7 +85,7 @@ template class RestoringGlobalStringComponent : public C if (hasdata) { this->value_.assign(temp + 1, temp[0]); } - this->prev_value_.assign(this->value_); + this->last_checked_value_.assign(this->value_); } float get_setup_priority() const override { return setup_priority::HARDWARE; } @@ -98,13 +98,12 @@ template class RestoringGlobalStringComponent : public C protected: void store_value_() { - int diff = this->value_.compare(this->prev_value_); + int diff = this->value_.compare(this->last_checked_value_); if (diff != 0) { // Make it into a length prefixed thing unsigned char temp[SZ]; // If string is bigger than the allocation, do not save it. - // We don't need to waste ram setting prev_value either. int size = this->value_.size(); // Less than, not less than or equal, SZ includes the length byte. if (size < SZ) { @@ -112,13 +111,17 @@ template class RestoringGlobalStringComponent : public C // SZ should be pre checked at the schema level, it can't go past the char range. temp[0] = ((unsigned char) size); this->rtc_.save(&temp); - this->prev_value_.assign(this->value_); } + // Always update last_checked_value_ to match current value, even for oversized strings. + // This prevents redundant size checks on every loop iteration when a string remains oversized. + // Without this, the diff != 0 check would pass repeatedly for the same oversized string, + // wasting CPU cycles on size comparisons. + this->last_checked_value_.assign(this->value_); } } T value_{}; - T prev_value_{}; + T last_checked_value_{}; uint32_t name_hash_{}; ESPPreferenceObject rtc_; }; diff --git a/esphome/components/gp2y1010au0f/gp2y1010au0f.cpp b/esphome/components/gp2y1010au0f/gp2y1010au0f.cpp index 95b7653e51..c8b0f13d3a 100644 --- a/esphome/components/gp2y1010au0f/gp2y1010au0f.cpp +++ b/esphome/components/gp2y1010au0f/gp2y1010au0f.cpp @@ -13,8 +13,10 @@ static const float MAX_VOLTAGE = 4.0f; void GP2Y1010AU0FSensor::dump_config() { LOG_SENSOR("", "Sharp GP2Y1010AU0F PM2.5 Sensor", this); - ESP_LOGCONFIG(TAG, " Sampling duration: %" PRId32 " ms", this->sample_duration_); - ESP_LOGCONFIG(TAG, " ADC voltage multiplier: %.3f", this->voltage_multiplier_); + ESP_LOGCONFIG(TAG, + " Sampling duration: %" PRId32 " ms\n" + " ADC voltage multiplier: %.3f", + this->sample_duration_, this->voltage_multiplier_); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/gp8403/gp8403.cpp b/esphome/components/gp8403/gp8403.cpp index 7a08a18a8f..5107e96dee 100644 --- a/esphome/components/gp8403/gp8403.cpp +++ b/esphome/components/gp8403/gp8403.cpp @@ -12,8 +12,10 @@ static const uint8_t RANGE_REGISTER = 0x01; void GP8403::setup() { this->write_register(RANGE_REGISTER, (uint8_t *) (&this->voltage_), 1); } void GP8403::dump_config() { - ESP_LOGCONFIG(TAG, "GP8403:"); - ESP_LOGCONFIG(TAG, " Voltage: %dV", this->voltage_ == GP8403_VOLTAGE_5V ? 5 : 10); + ESP_LOGCONFIG(TAG, + "GP8403:\n" + " Voltage: %dV", + this->voltage_ == GP8403_VOLTAGE_5V ? 5 : 10); LOG_I2C_DEVICE(this); } diff --git a/esphome/components/gp8403/output/gp8403_output.cpp b/esphome/components/gp8403/output/gp8403_output.cpp index ff73bb4627..edb6972184 100644 --- a/esphome/components/gp8403/output/gp8403_output.cpp +++ b/esphome/components/gp8403/output/gp8403_output.cpp @@ -10,8 +10,10 @@ static const char *const TAG = "gp8403.output"; static const uint8_t OUTPUT_REGISTER = 0x02; void GP8403Output::dump_config() { - ESP_LOGCONFIG(TAG, "GP8403 Output:"); - ESP_LOGCONFIG(TAG, " Channel: %u", this->channel_); + ESP_LOGCONFIG(TAG, + "GP8403 Output:\n" + " Channel: %u", + this->channel_); } void GP8403Output::write_state(float state) { diff --git a/esphome/components/gpio/one_wire/gpio_one_wire.cpp b/esphome/components/gpio/one_wire/gpio_one_wire.cpp index 8a56595efb..ee80fde6fa 100644 --- a/esphome/components/gpio/one_wire/gpio_one_wire.cpp +++ b/esphome/components/gpio/one_wire/gpio_one_wire.cpp @@ -1,6 +1,6 @@ #include "gpio_one_wire.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace gpio { @@ -8,7 +8,7 @@ namespace gpio { static const char *const TAG = "gpio.one_wire"; void GPIOOneWireBus::setup() { - ESP_LOGCONFIG(TAG, "Setting up 1-wire bus..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->t_pin_->setup(); this->t_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); // clear bus with 480µs high, otherwise initial reset in search might fail diff --git a/esphome/components/gpio/output/gpio_binary_output.cpp b/esphome/components/gpio/output/gpio_binary_output.cpp index a7dd9ab188..13538b6f2b 100644 --- a/esphome/components/gpio/output/gpio_binary_output.cpp +++ b/esphome/components/gpio/output/gpio_binary_output.cpp @@ -7,7 +7,7 @@ namespace gpio { static const char *const TAG = "gpio.output"; void GPIOBinaryOutput::dump_config() { - ESP_LOGCONFIG(TAG, "GPIO Binary Output:"); + ESP_LOGCONFIG(TAG, "Binary Output:"); LOG_PIN(" Pin: ", this->pin_); LOG_BINARY_OUTPUT(this); } diff --git a/esphome/components/gpio/switch/gpio_switch.cpp b/esphome/components/gpio/switch/gpio_switch.cpp index 5033315b5e..6f901d602d 100644 --- a/esphome/components/gpio/switch/gpio_switch.cpp +++ b/esphome/components/gpio/switch/gpio_switch.cpp @@ -8,7 +8,7 @@ static const char *const TAG = "switch.gpio"; float GPIOSwitch::get_setup_priority() const { return setup_priority::HARDWARE; } void GPIOSwitch::setup() { - ESP_LOGCONFIG(TAG, "Setting up GPIO Switch '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); bool initial_state = this->get_initial_state_with_restore_mode().value_or(false); diff --git a/esphome/components/graphical_display_menu/graphical_display_menu.cpp b/esphome/components/graphical_display_menu/graphical_display_menu.cpp index 4a4e519009..1a29536b46 100644 --- a/esphome/components/graphical_display_menu/graphical_display_menu.cpp +++ b/esphome/components/graphical_display_menu/graphical_display_menu.cpp @@ -36,14 +36,18 @@ void GraphicalDisplayMenu::setup() { } void GraphicalDisplayMenu::dump_config() { - ESP_LOGCONFIG(TAG, "Graphical Display Menu"); - ESP_LOGCONFIG(TAG, "Has Display: %s", YESNO(this->display_ != nullptr)); - ESP_LOGCONFIG(TAG, "Popup Mode: %s", YESNO(this->display_ != nullptr)); - ESP_LOGCONFIG(TAG, "Advanced Drawing Mode: %s", YESNO(this->display_ == nullptr)); - ESP_LOGCONFIG(TAG, "Has Font: %s", YESNO(this->font_ != nullptr)); - ESP_LOGCONFIG(TAG, "Mode: %s", this->mode_ == display_menu_base::MENU_MODE_ROTARY ? "Rotary" : "Joystick"); - ESP_LOGCONFIG(TAG, "Active: %s", YESNO(this->active_)); - ESP_LOGCONFIG(TAG, "Menu items:"); + ESP_LOGCONFIG(TAG, + "Graphical Display Menu\n" + "Has Display: %s\n" + "Popup Mode: %s\n" + "Advanced Drawing Mode: %s\n" + "Has Font: %s\n" + "Mode: %s\n" + "Active: %s\n" + "Menu items:", + YESNO(this->display_ != nullptr), YESNO(this->display_ != nullptr), YESNO(this->display_ == nullptr), + YESNO(this->font_ != nullptr), + this->mode_ == display_menu_base::MENU_MODE_ROTARY ? "Rotary" : "Joystick", YESNO(this->active_)); for (size_t i = 0; i < this->displayed_item_->items_size(); i++) { auto *item = this->displayed_item_->get_item(i); ESP_LOGCONFIG(TAG, " %i: %s (Type: %s, Immediate Edit: %s)", i, item->get_text().c_str(), diff --git a/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp b/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp index ed40ba42a5..361f3e04fd 100644 --- a/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +++ b/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp @@ -33,8 +33,7 @@ bool GroveGasMultichannelV2Component::read_sensor_(uint8_t address, sensor::Sens } void GroveGasMultichannelV2Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up Grove Multichannel Gas Sensor V2..."); - + ESP_LOGCONFIG(TAG, "Running setup"); // Before reading sensor values, must preheat sensor if (!(this->write_bytes(GROVE_GAS_MC_V2_HEAT_ON, {}))) { this->mark_failed(); @@ -68,17 +67,17 @@ void GroveGasMultichannelV2Component::dump_config() { if (this->is_failed()) { switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGW(TAG, "Communication failed! Is the sensor connected?"); + ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); break; case APP_INVALID: - ESP_LOGW(TAG, "Sensor reported invalid APP installed."); + ESP_LOGW(TAG, "Sensor reported invalid APP installed"); break; case APP_START_FAILED: - ESP_LOGW(TAG, "Sensor reported APP start failed."); + ESP_LOGW(TAG, "Sensor reported APP start failed"); break; case UNKNOWN: default: - ESP_LOGW(TAG, "Unknown setup error!"); + ESP_LOGW(TAG, "Unknown setup error"); break; } } diff --git a/esphome/components/grove_tb6612fng/grove_tb6612fng.cpp b/esphome/components/grove_tb6612fng/grove_tb6612fng.cpp index 621b7968a4..0dfb8478e7 100644 --- a/esphome/components/grove_tb6612fng/grove_tb6612fng.cpp +++ b/esphome/components/grove_tb6612fng/grove_tb6612fng.cpp @@ -24,7 +24,7 @@ void GroveMotorDriveTB6612FNG::dump_config() { } void GroveMotorDriveTB6612FNG::setup() { - ESP_LOGCONFIG(TAG, "Setting up Grove Motor Drive TB6612FNG ..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->standby()) { this->mark_failed(); return; diff --git a/esphome/components/grove_tb6612fng/grove_tb6612fng.h b/esphome/components/grove_tb6612fng/grove_tb6612fng.h index 2743ef4ed7..68281117e7 100644 --- a/esphome/components/grove_tb6612fng/grove_tb6612fng.h +++ b/esphome/components/grove_tb6612fng/grove_tb6612fng.h @@ -1,9 +1,9 @@ #pragma once #include "esphome/components/i2c/i2c.h" +#include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" -#include "esphome/core/automation.h" //#include "esphome/core/helpers.h" /* diff --git a/esphome/components/growatt_solar/growatt_solar.cpp b/esphome/components/growatt_solar/growatt_solar.cpp index 60fd1379e8..686c1c232e 100644 --- a/esphome/components/growatt_solar/growatt_solar.cpp +++ b/esphome/components/growatt_solar/growatt_solar.cpp @@ -1,6 +1,7 @@ #include "growatt_solar.h" -#include "esphome/core/log.h" #include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace growatt_solar { @@ -134,8 +135,10 @@ void GrowattSolar::on_modbus_data(const std::vector &data) { } void GrowattSolar::dump_config() { - ESP_LOGCONFIG(TAG, "GROWATT Solar:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "GROWATT Solar:\n" + " Address: 0x%02X", + this->address_); } } // namespace growatt_solar diff --git a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp index 674ef51d64..1cead70181 100644 --- a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +++ b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp @@ -25,7 +25,7 @@ static const size_t MAX_BUTTONS = 4; // max number of buttons scanned void GT911Touchscreen::setup() { i2c::ErrorCode err; - ESP_LOGCONFIG(TAG, "Setting up GT911 Touchscreen..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (this->reset_pin_ != nullptr) { this->reset_pin_->setup(); this->reset_pin_->digital_write(false); diff --git a/esphome/components/haier/haier_base.cpp b/esphome/components/haier/haier_base.cpp index f8c0a7587e..a784accdf4 100644 --- a/esphome/components/haier/haier_base.cpp +++ b/esphome/components/haier/haier_base.cpp @@ -242,7 +242,7 @@ haier_protocol::HandlerError HaierClimateBase::timeout_default_handler_(haier_pr } void HaierClimateBase::setup() { - ESP_LOGI(TAG, "Haier initialization..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Set timestamp here to give AC time to boot this->last_request_timestamp_ = std::chrono::steady_clock::now(); this->set_phase(ProtocolPhases::SENDING_INIT_1); @@ -286,7 +286,7 @@ void HaierClimateBase::loop() { if (this->action_request_.has_value() && this->prepare_pending_action()) { this->set_phase(ProtocolPhases::SENDING_ACTION_COMMAND); } else if (this->next_hvac_settings_.valid || this->force_send_control_) { - ESP_LOGV(TAG, "Control packet is pending..."); + ESP_LOGV(TAG, "Control packet is pending"); this->set_phase(ProtocolPhases::SENDING_CONTROL); if (this->next_hvac_settings_.valid) { this->current_hvac_settings_ = this->next_hvac_settings_; diff --git a/esphome/components/haier/hon_climate.cpp b/esphome/components/haier/hon_climate.cpp index 9b59dd0c10..fd2d6a5800 100644 --- a/esphome/components/haier/hon_climate.cpp +++ b/esphome/components/haier/hon_climate.cpp @@ -339,13 +339,20 @@ void HonClimate::set_handlers() { void HonClimate::dump_config() { HaierClimateBase::dump_config(); - ESP_LOGCONFIG(TAG, " Protocol version: hOn"); - ESP_LOGCONFIG(TAG, " Control method: %d", (uint8_t) this->control_method_); + ESP_LOGCONFIG(TAG, + " Protocol version: hOn\n" + " Control method: %d", + (uint8_t) this->control_method_); if (this->hvac_hardware_info_.has_value()) { - ESP_LOGCONFIG(TAG, " Device protocol version: %s", this->hvac_hardware_info_.value().protocol_version_.c_str()); - ESP_LOGCONFIG(TAG, " Device software version: %s", this->hvac_hardware_info_.value().software_version_.c_str()); - ESP_LOGCONFIG(TAG, " Device hardware version: %s", this->hvac_hardware_info_.value().hardware_version_.c_str()); - ESP_LOGCONFIG(TAG, " Device name: %s", this->hvac_hardware_info_.value().device_name_.c_str()); + ESP_LOGCONFIG(TAG, + " Device protocol version: %s\n" + " Device software version: %s\n" + " Device hardware version: %s\n" + " Device name: %s", + this->hvac_hardware_info_.value().protocol_version_.c_str(), + this->hvac_hardware_info_.value().software_version_.c_str(), + this->hvac_hardware_info_.value().hardware_version_.c_str(), + this->hvac_hardware_info_.value().device_name_.c_str()); ESP_LOGCONFIG(TAG, " Device features:%s%s%s%s%s", (this->hvac_hardware_info_.value().functions_[0] ? " interactive" : ""), (this->hvac_hardware_info_.value().functions_[1] ? " controller-device" : ""), diff --git a/esphome/components/havells_solar/havells_solar.cpp b/esphome/components/havells_solar/havells_solar.cpp index f029df10ad..20dddf39ed 100644 --- a/esphome/components/havells_solar/havells_solar.cpp +++ b/esphome/components/havells_solar/havells_solar.cpp @@ -1,5 +1,6 @@ #include "havells_solar.h" #include "havells_solar_registers.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -123,8 +124,10 @@ void HavellsSolar::on_modbus_data(const std::vector &data) { void HavellsSolar::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); } void HavellsSolar::dump_config() { - ESP_LOGCONFIG(TAG, "HAVELLS Solar:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "HAVELLS Solar:\n" + " Address: 0x%02X", + this->address_); for (uint8_t i = 0; i < 3; i++) { auto phase = this->phases_[i]; if (!phase.setup) diff --git a/esphome/components/hbridge/switch/hbridge_switch.cpp b/esphome/components/hbridge/switch/hbridge_switch.cpp index c0949be947..2a1afa48c5 100644 --- a/esphome/components/hbridge/switch/hbridge_switch.cpp +++ b/esphome/components/hbridge/switch/hbridge_switch.cpp @@ -10,7 +10,7 @@ static const char *const TAG = "switch.hbridge"; float HBridgeSwitch::get_setup_priority() const { return setup_priority::HARDWARE; } void HBridgeSwitch::setup() { - ESP_LOGCONFIG(TAG, "Setting up H-Bridge Switch '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); optional initial_state = this->get_initial_state_with_restore_mode(); diff --git a/esphome/components/hdc1080/hdc1080.cpp b/esphome/components/hdc1080/hdc1080.cpp index 7186578a22..956d01ed82 100644 --- a/esphome/components/hdc1080/hdc1080.cpp +++ b/esphome/components/hdc1080/hdc1080.cpp @@ -13,7 +13,7 @@ static const uint8_t HDC1080_CMD_TEMPERATURE = 0x00; static const uint8_t HDC1080_CMD_HUMIDITY = 0x01; void HDC1080Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up HDC1080..."); + ESP_LOGCONFIG(TAG, "Running setup"); const uint8_t data[2] = { 0b00000000, // resolution 14bit for both humidity and temperature @@ -31,7 +31,7 @@ void HDC1080Component::dump_config() { ESP_LOGCONFIG(TAG, "HDC1080:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with HDC1080 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_); diff --git a/esphome/components/he60r/he60r.cpp b/esphome/components/he60r/he60r.cpp index 83e895543d..ca17930272 100644 --- a/esphome/components/he60r/he60r.cpp +++ b/esphome/components/he60r/he60r.cpp @@ -40,8 +40,10 @@ CoverTraits HE60rCover::get_traits() { void HE60rCover::dump_config() { LOG_COVER("", "HE60R Cover", this); this->check_uart_settings(1200, 1, uart::UART_CONFIG_PARITY_EVEN, 8); - ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f); - ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f); + ESP_LOGCONFIG(TAG, + " Open Duration: %.1fs\n" + " Close Duration: %.1fs", + this->open_duration_ / 1e3f, this->close_duration_ / 1e3f); auto restore = this->restore_state_(); if (restore.has_value()) ESP_LOGCONFIG(TAG, " Saved position %d%%", (int) (restore->position * 100.f)); diff --git a/esphome/components/hlw8012/hlw8012.cpp b/esphome/components/hlw8012/hlw8012.cpp index 1efc57ab66..ea1d081790 100644 --- a/esphome/components/hlw8012/hlw8012.cpp +++ b/esphome/components/hlw8012/hlw8012.cpp @@ -11,7 +11,7 @@ static const uint32_t HLW8012_CLOCK_FREQUENCY = 3579000; void HLW8012Component::setup() { float reference_voltage = 0; - ESP_LOGCONFIG(TAG, "Setting up HLW8012..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->sel_pin_->setup(); this->sel_pin_->digital_write(this->current_mode_); this->cf_store_.pulse_counter_setup(this->cf_pin_); @@ -38,9 +38,11 @@ void HLW8012Component::dump_config() { LOG_PIN(" SEL Pin: ", this->sel_pin_) LOG_PIN(" CF Pin: ", this->cf_pin_) LOG_PIN(" CF1 Pin: ", this->cf1_pin_) - ESP_LOGCONFIG(TAG, " Change measurement mode every %" PRIu32, this->change_mode_every_); - ESP_LOGCONFIG(TAG, " Current resistor: %.1f mΩ", this->current_resistor_ * 1000.0f); - ESP_LOGCONFIG(TAG, " Voltage Divider: %.1f", this->voltage_divider_); + ESP_LOGCONFIG(TAG, + " Change measurement mode every %" PRIu32 "\n" + " Current resistor: %.1f mΩ\n" + " Voltage Divider: %.1f", + this->change_mode_every_, this->current_resistor_ * 1000.0f, this->voltage_divider_); LOG_UPDATE_INTERVAL(this) LOG_SENSOR(" ", "Voltage", this->voltage_sensor_) LOG_SENSOR(" ", "Current", this->current_sensor_) diff --git a/esphome/components/hm3301/hm3301.cpp b/esphome/components/hm3301/hm3301.cpp index 379c4dbc5a..b165e361ff 100644 --- a/esphome/components/hm3301/hm3301.cpp +++ b/esphome/components/hm3301/hm3301.cpp @@ -11,7 +11,7 @@ static const uint8_t PM_2_5_VALUE_INDEX = 6; static const uint8_t PM_10_0_VALUE_INDEX = 7; void HM3301Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up HM3301..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (i2c::ERROR_OK != this->write(&SELECT_COMM_CMD, 1)) { error_code_ = ERROR_COMM; this->mark_failed(); @@ -23,7 +23,7 @@ void HM3301Component::dump_config() { ESP_LOGCONFIG(TAG, "HM3301:"); LOG_I2C_DEVICE(this); if (error_code_ == ERROR_COMM) { - ESP_LOGE(TAG, "Communication with HM3301 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_SENSOR(" ", "PM1.0", this->pm_1_0_sensor_); diff --git a/esphome/components/hmc5883l/hmc5883l.cpp b/esphome/components/hmc5883l/hmc5883l.cpp index 24f4b3f8f1..fe90b25af2 100644 --- a/esphome/components/hmc5883l/hmc5883l.cpp +++ b/esphome/components/hmc5883l/hmc5883l.cpp @@ -22,7 +22,7 @@ static const uint8_t HMC5883L_REGISTER_IDENTIFICATION_B = 0x0B; static const uint8_t HMC5883L_REGISTER_IDENTIFICATION_C = 0x0C; void HMC5883LComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up HMC5583L..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t id[3]; if (!this->read_byte(HMC5883L_REGISTER_IDENTIFICATION_A, &id[0]) || !this->read_byte(HMC5883L_REGISTER_IDENTIFICATION_B, &id[1]) || @@ -73,7 +73,7 @@ void HMC5883LComponent::dump_config() { ESP_LOGCONFIG(TAG, "HMC5883L:"); LOG_I2C_DEVICE(this); if (this->error_code_ == COMMUNICATION_FAILED) { - ESP_LOGE(TAG, "Communication with HMC5883L failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } else if (this->error_code_ == ID_REGISTERS) { ESP_LOGE(TAG, "The ID registers don't match - Is this really an HMC5883L?"); } diff --git a/esphome/components/homeassistant/time/homeassistant_time.cpp b/esphome/components/homeassistant/time/homeassistant_time.cpp index 9f5239404a..0a91a2f63d 100644 --- a/esphome/components/homeassistant/time/homeassistant_time.cpp +++ b/esphome/components/homeassistant/time/homeassistant_time.cpp @@ -7,8 +7,10 @@ namespace homeassistant { static const char *const TAG = "homeassistant.time"; void HomeassistantTime::dump_config() { - ESP_LOGCONFIG(TAG, "Home Assistant Time:"); - ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); + ESP_LOGCONFIG(TAG, + "Home Assistant Time:\n" + " Timezone: '%s'", + this->timezone_.c_str()); } float HomeassistantTime::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/honeywell_hih_i2c/honeywell_hih.cpp b/esphome/components/honeywell_hih_i2c/honeywell_hih.cpp index 64d5ddb541..5f2b009972 100644 --- a/esphome/components/honeywell_hih_i2c/honeywell_hih.cpp +++ b/esphome/components/honeywell_hih_i2c/honeywell_hih.cpp @@ -17,7 +17,7 @@ void HoneywellHIComponent::read_sensor_data_() { uint8_t data[4]; if (this->read(data, sizeof(data)) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Communication with Honeywell HIH failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); return; } @@ -37,7 +37,7 @@ void HoneywellHIComponent::read_sensor_data_() { void HoneywellHIComponent::start_measurement_() { if (this->write(REQUEST_CMD, sizeof(REQUEST_CMD)) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Communication with Honeywell HIH failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); return; } @@ -49,7 +49,7 @@ bool HoneywellHIComponent::is_measurement_ready_() { uint8_t data[1]; if (this->read(data, sizeof(data)) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Communication with Honeywell HIH failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); return false; } @@ -84,7 +84,7 @@ void HoneywellHIComponent::dump_config() { ESP_LOGD(TAG, "Honeywell HIH:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with Honeywell HIH failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); diff --git a/esphome/components/honeywellabp/honeywellabp.cpp b/esphome/components/honeywellabp/honeywellabp.cpp index 124bd6bb95..9252e613dd 100644 --- a/esphome/components/honeywellabp/honeywellabp.cpp +++ b/esphome/components/honeywellabp/honeywellabp.cpp @@ -85,8 +85,10 @@ float HONEYWELLABPSensor::get_setup_priority() const { return setup_priority::LA void HONEYWELLABPSensor::dump_config() { // LOG_SENSOR("", "HONEYWELLABP", this); LOG_PIN(" CS Pin: ", this->cs_); - ESP_LOGCONFIG(TAG, " Min Pressure Range: %0.1f", honeywellabp_min_pressure_); - ESP_LOGCONFIG(TAG, " Max Pressure Range: %0.1f", honeywellabp_max_pressure_); + ESP_LOGCONFIG(TAG, + " Min Pressure Range: %0.1f\n" + " Max Pressure Range: %0.1f", + honeywellabp_min_pressure_, honeywellabp_max_pressure_); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp b/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp index d111723669..11f5dbc314 100644 --- a/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp +++ b/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp @@ -1,6 +1,6 @@ #include "honeywellabp2.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace honeywellabp2_i2c { @@ -14,7 +14,7 @@ static const char *const TAG = "honeywellabp2"; void HONEYWELLABP2Sensor::read_sensor_data() { if (this->read(raw_data_, 7) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Communication with ABP2 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning("couldn't read sensor data"); return; } @@ -30,7 +30,7 @@ void HONEYWELLABP2Sensor::read_sensor_data() { void HONEYWELLABP2Sensor::start_measurement() { if (this->write(i2c_cmd_, 3) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Communication with ABP2 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning("couldn't start measurement"); return; } @@ -39,7 +39,7 @@ void HONEYWELLABP2Sensor::start_measurement() { bool HONEYWELLABP2Sensor::is_measurement_ready() { if (this->read(raw_data_, 1) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Communication with ABP2 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning("couldn't check measurement"); return false; } @@ -84,8 +84,10 @@ void HONEYWELLABP2Sensor::update() { } void HONEYWELLABP2Sensor::dump_config() { - ESP_LOGCONFIG(TAG, " Min Pressure Range: %0.1f", this->min_pressure_); - ESP_LOGCONFIG(TAG, " Max Pressure Range: %0.1f", this->max_pressure_); + ESP_LOGCONFIG(TAG, + " Min Pressure Range: %0.1f\n" + " Max Pressure Range: %0.1f", + this->min_pressure_, this->max_pressure_); if (this->transfer_function_ == ABP2_TRANS_FUNC_A) { ESP_LOGCONFIG(TAG, " Transfer function A"); } else { diff --git a/esphome/components/hte501/hte501.cpp b/esphome/components/hte501/hte501.cpp index 68edd07a22..0f97c67f9e 100644 --- a/esphome/components/hte501/hte501.cpp +++ b/esphome/components/hte501/hte501.cpp @@ -1,4 +1,5 @@ #include "hte501.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -7,7 +8,7 @@ namespace hte501 { static const char *const TAG = "hte501"; void HTE501Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up HTE501..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t address[] = {0x70, 0x29}; this->write(address, 2, false); uint8_t identification[9]; @@ -25,7 +26,7 @@ void HTE501Component::dump_config() { LOG_I2C_DEVICE(this); switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Communication with HTE501 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case CRC_CHECK_FAILED: ESP_LOGE(TAG, "The crc check failed"); diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index 9aa0c42fa2..ac13334118 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -1,6 +1,7 @@ from esphome import automation import esphome.codegen as cg from esphome.components import esp32 +from esphome.components.const import CONF_REQUEST_HEADERS import esphome.config_validation as cv from esphome.const import ( CONF_ESP8266_DISABLE_SSL_SUPPORT, @@ -10,6 +11,7 @@ from esphome.const import ( CONF_TIMEOUT, CONF_TRIGGER_ID, CONF_URL, + CONF_WATCHDOG_TIMEOUT, PLATFORM_HOST, __version__, ) @@ -43,7 +45,6 @@ CONF_USERAGENT = "useragent" CONF_VERIFY_SSL = "verify_ssl" CONF_FOLLOW_REDIRECTS = "follow_redirects" CONF_REDIRECT_LIMIT = "redirect_limit" -CONF_WATCHDOG_TIMEOUT = "watchdog_timeout" CONF_BUFFER_SIZE_RX = "buffer_size_rx" CONF_BUFFER_SIZE_TX = "buffer_size_tx" CONF_CA_CERTIFICATE_PATH = "ca_certificate_path" @@ -51,7 +52,6 @@ CONF_CA_CERTIFICATE_PATH = "ca_certificate_path" CONF_MAX_RESPONSE_BUFFER_SIZE = "max_response_buffer_size" CONF_ON_RESPONSE = "on_response" CONF_HEADERS = "headers" -CONF_REQUEST_HEADERS = "request_headers" CONF_COLLECT_HEADERS = "collect_headers" CONF_BODY = "body" CONF_JSON = "json" diff --git a/esphome/components/http_request/http_request.cpp b/esphome/components/http_request/http_request.cpp index ca9fd2c2dc..806354baf1 100644 --- a/esphome/components/http_request/http_request.cpp +++ b/esphome/components/http_request/http_request.cpp @@ -10,11 +10,13 @@ namespace http_request { static const char *const TAG = "http_request"; void HttpRequestComponent::dump_config() { - ESP_LOGCONFIG(TAG, "HTTP Request:"); - ESP_LOGCONFIG(TAG, " Timeout: %ums", this->timeout_); - ESP_LOGCONFIG(TAG, " User-Agent: %s", this->useragent_); - ESP_LOGCONFIG(TAG, " Follow redirects: %s", YESNO(this->follow_redirects_)); - ESP_LOGCONFIG(TAG, " Redirect limit: %d", this->redirect_limit_); + ESP_LOGCONFIG(TAG, + "HTTP Request:\n" + " Timeout: %ums\n" + " User-Agent: %s\n" + " Follow redirects: %s\n" + " Redirect limit: %d", + this->timeout_, this->useragent_, YESNO(this->follow_redirects_), this->redirect_limit_); if (this->watchdog_timeout_ > 0) { ESP_LOGCONFIG(TAG, " Watchdog Timeout: %" PRIu32 "ms", this->watchdog_timeout_); } diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index 0923062822..6a779ba03a 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -26,8 +26,10 @@ struct UserData { void HttpRequestIDF::dump_config() { HttpRequestComponent::dump_config(); - ESP_LOGCONFIG(TAG, " Buffer Size RX: %u", this->buffer_size_rx_); - ESP_LOGCONFIG(TAG, " Buffer Size TX: %u", this->buffer_size_tx_); + ESP_LOGCONFIG(TAG, + " Buffer Size RX: %u\n" + " Buffer Size TX: %u", + this->buffer_size_rx_, this->buffer_size_tx_); } esp_err_t HttpRequestIDF::http_event_handler(esp_http_client_event_t *evt) { diff --git a/esphome/components/http_request/ota/ota_http_request.cpp b/esphome/components/http_request/ota/ota_http_request.cpp index cec30d72ec..4c8d49dad5 100644 --- a/esphome/components/http_request/ota/ota_http_request.cpp +++ b/esphome/components/http_request/ota/ota_http_request.cpp @@ -48,7 +48,7 @@ void OtaHttpRequestComponent::flash() { return; } - ESP_LOGI(TAG, "Starting update..."); + ESP_LOGI(TAG, "Starting update"); #ifdef USE_OTA_STATE_CALLBACK this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0); #endif diff --git a/esphome/components/htu21d/htu21d.cpp b/esphome/components/htu21d/htu21d.cpp index 411d1e1d6a..b5d6ad45d5 100644 --- a/esphome/components/htu21d/htu21d.cpp +++ b/esphome/components/htu21d/htu21d.cpp @@ -18,7 +18,7 @@ static const uint8_t HTU21D_READHEATER_REG_CMD = 0x11; /**< Read Heater Control static const uint8_t HTU21D_REG_HTRE_BIT = 0x02; /**< Control Register Heater Bit */ void HTU21DComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up HTU21D..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->write_bytes(HTU21D_REGISTER_RESET, nullptr, 0)) { this->mark_failed(); @@ -32,7 +32,7 @@ void HTU21DComponent::dump_config() { ESP_LOGCONFIG(TAG, "HTU21D:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with HTU21D failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_); diff --git a/esphome/components/htu31d/htu31d.cpp b/esphome/components/htu31d/htu31d.cpp index bf4689d837..284548ed96 100644 --- a/esphome/components/htu31d/htu31d.cpp +++ b/esphome/components/htu31d/htu31d.cpp @@ -75,7 +75,7 @@ uint8_t compute_crc(uint32_t value) { * I2C. */ void HTU31DComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up esphome/components/htu31d HTU31D..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->reset_()) { this->mark_failed(); @@ -161,7 +161,7 @@ void HTU31DComponent::dump_config() { ESP_LOGCONFIG(TAG, "HTU31D:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with HTU31D failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_); diff --git a/esphome/components/hx711/hx711.cpp b/esphome/components/hx711/hx711.cpp index 9643d0c411..0fc8b29604 100644 --- a/esphome/components/hx711/hx711.cpp +++ b/esphome/components/hx711/hx711.cpp @@ -1,6 +1,6 @@ #include "hx711.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace hx711 { @@ -8,7 +8,7 @@ namespace hx711 { static const char *const TAG = "hx711"; void HX711Sensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up HX711 '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); this->sck_pin_->setup(); this->dout_pin_->setup(); this->sck_pin_->digital_write(false); diff --git a/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp b/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp index 92d7774193..2d8381b60c 100644 --- a/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +++ b/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp @@ -18,8 +18,10 @@ void HydreonRGxxComponent::dump_config() { ESP_LOGE(TAG, "Connection with hydreon_rgxx failed!"); } if (model_ == RG9) { - ESP_LOGCONFIG(TAG, " Model: RG9"); - ESP_LOGCONFIG(TAG, " Disable Led: %s", TRUEFALSE(this->disable_led_)); + ESP_LOGCONFIG(TAG, + " Model: RG9\n" + " Disable Led: %s", + TRUEFALSE(this->disable_led_)); } else { ESP_LOGCONFIG(TAG, " Model: RG15"); if (this->resolution_ == FORCE_HIGH) { @@ -39,7 +41,7 @@ void HydreonRGxxComponent::dump_config() { } void HydreonRGxxComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up hydreon_rgxx..."); + ESP_LOGCONFIG(TAG, "Running setup"); while (this->available() != 0) { this->read(); } diff --git a/esphome/components/hyt271/hyt271.cpp b/esphome/components/hyt271/hyt271.cpp index 3b81294cfc..f187e054a8 100644 --- a/esphome/components/hyt271/hyt271.cpp +++ b/esphome/components/hyt271/hyt271.cpp @@ -21,14 +21,14 @@ void HYT271Component::update() { if (this->write(&raw_data[0], 0) != i2c::ERROR_OK) { this->status_set_warning(); - ESP_LOGE(TAG, "Communication with HYT271 failed! => Ask new values"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); return; } this->set_timeout("wait_convert", 50, [this]() { uint8_t raw_data[4]; if (this->read(raw_data, 4) != i2c::ERROR_OK) { this->status_set_warning(); - ESP_LOGE(TAG, "Communication with HYT271 failed! => Read values"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); return; } uint16_t raw_temperature = ((raw_data[2] << 8) | raw_data[3]) >> 2; diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp index cd1b2aacc7..dca77e878d 100644 --- a/esphome/components/i2c/i2c_bus_arduino.cpp +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -1,9 +1,9 @@ #ifdef USE_ARDUINO #include "i2c_bus_arduino.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include #include @@ -13,6 +13,7 @@ namespace i2c { static const char *const TAG = "i2c.arduino"; void ArduinoI2CBus::setup() { + ESP_LOGCONFIG(TAG, "Running setup"); recover_(); #if defined(USE_ESP32) @@ -39,7 +40,7 @@ void ArduinoI2CBus::setup() { this->initialized_ = true; if (this->scan_) { - ESP_LOGV(TAG, "Scanning i2c bus for active devices..."); + ESP_LOGV(TAG, "Scanning bus for active devices"); this->i2c_scan_(); } } @@ -69,9 +70,11 @@ void ArduinoI2CBus::set_pins_and_clock_() { void ArduinoI2CBus::dump_config() { ESP_LOGCONFIG(TAG, "I2C Bus:"); - ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); - ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); - ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_); + ESP_LOGCONFIG(TAG, + " SDA Pin: GPIO%u\n" + " SCL Pin: GPIO%u\n" + " Frequency: %u Hz", + this->sda_pin_, this->scl_pin_, this->frequency_); if (timeout_ > 0) { #if defined(USE_ESP32) ESP_LOGCONFIG(TAG, " Timeout: %u ms", this->timeout_ / 1000); @@ -93,13 +96,13 @@ void ArduinoI2CBus::dump_config() { break; } if (this->scan_) { - ESP_LOGI(TAG, "Results from i2c bus scan:"); + ESP_LOGI(TAG, "Results from bus scan:"); if (scan_results_.empty()) { - ESP_LOGI(TAG, "Found no i2c devices!"); + ESP_LOGI(TAG, "Found no devices"); } else { for (const auto &s : scan_results_) { if (s.second) { - ESP_LOGI(TAG, "Found i2c device at address 0x%02X", s.first); + ESP_LOGI(TAG, "Found device at address 0x%02X", s.first); } else { ESP_LOGE(TAG, "Unknown error at address 0x%02X", s.first); } @@ -215,7 +218,7 @@ ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cn /// https://www.nxp.com/docs/en/user-guide/UM10204.pdf /// https://www.analog.com/media/en/technical-documentation/application-notes/54305147357414AN686_0.pdf void ArduinoI2CBus::recover_() { - ESP_LOGI(TAG, "Performing I2C bus recovery"); + ESP_LOGI(TAG, "Performing bus recovery"); // For the upcoming operations, target for a 100kHz toggle frequency. // This is the maximum frequency for I2C running in standard-mode. @@ -231,7 +234,7 @@ void ArduinoI2CBus::recover_() { // line. In that case, the I2C bus cannot be recovered. delayMicroseconds(half_period_usec); if (digitalRead(scl_pin_) == LOW) { // NOLINT - ESP_LOGE(TAG, "Recovery failed: SCL is held LOW on the I2C bus"); + ESP_LOGE(TAG, "Recovery failed: SCL is held LOW on the bus"); recovery_result_ = RECOVERY_FAILED_SCL_LOW; return; } diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp index c14300f725..e4643405ce 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.cpp +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -18,7 +18,7 @@ namespace i2c { static const char *const TAG = "i2c.idf"; void IDFI2CBus::setup() { - ESP_LOGCONFIG(TAG, "Setting up I2C bus..."); + ESP_LOGCONFIG(TAG, "Running setup"); static i2c_port_t next_port = I2C_NUM_0; port_ = next_port; #if SOC_HP_I2C_NUM > 1 @@ -28,7 +28,7 @@ void IDFI2CBus::setup() { #endif if (port_ == I2C_NUM_MAX) { - ESP_LOGE(TAG, "Too many I2C buses configured. Max %u supported.", SOC_HP_I2C_NUM); + ESP_LOGE(TAG, "No more than %u buses supported", SOC_HP_I2C_NUM); this->mark_failed(); return; } @@ -75,15 +75,17 @@ void IDFI2CBus::setup() { } initialized_ = true; if (this->scan_) { - ESP_LOGV(TAG, "Scanning i2c bus for active devices..."); + ESP_LOGV(TAG, "Scanning bus for active devices"); this->i2c_scan_(); } } void IDFI2CBus::dump_config() { ESP_LOGCONFIG(TAG, "I2C Bus:"); - ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); - ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); - ESP_LOGCONFIG(TAG, " Frequency: %" PRIu32 " Hz", this->frequency_); + ESP_LOGCONFIG(TAG, + " SDA Pin: GPIO%u\n" + " SCL Pin: GPIO%u\n" + " Frequency: %" PRIu32 " Hz", + this->sda_pin_, this->scl_pin_, this->frequency_); if (timeout_ > 0) { ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 "us", this->timeout_); } @@ -99,13 +101,13 @@ void IDFI2CBus::dump_config() { break; } if (this->scan_) { - ESP_LOGI(TAG, "Results from i2c bus scan:"); + ESP_LOGI(TAG, "Results from bus scan:"); if (scan_results_.empty()) { - ESP_LOGI(TAG, "Found no i2c devices!"); + ESP_LOGI(TAG, "Found no devices"); } else { for (const auto &s : scan_results_) { if (s.second) { - ESP_LOGI(TAG, "Found i2c device at address 0x%02X", s.first); + ESP_LOGI(TAG, "Found device at address 0x%02X", s.first); } else { ESP_LOGE(TAG, "Unknown error at address 0x%02X", s.first); } @@ -257,7 +259,7 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, b /// https://www.nxp.com/docs/en/user-guide/UM10204.pdf /// https://www.analog.com/media/en/technical-documentation/application-notes/54305147357414AN686_0.pdf void IDFI2CBus::recover_() { - ESP_LOGI(TAG, "Performing I2C bus recovery"); + ESP_LOGI(TAG, "Performing bus recovery"); const gpio_num_t scl_pin = static_cast(scl_pin_); const gpio_num_t sda_pin = static_cast(sda_pin_); @@ -294,7 +296,7 @@ void IDFI2CBus::recover_() { // with the SCL line. In that case, the I2C bus cannot be recovered. delayMicroseconds(half_period_usec); if (gpio_get_level(scl_pin) == 0) { - ESP_LOGE(TAG, "Recovery failed: SCL is held LOW on the I2C bus"); + ESP_LOGE(TAG, "Recovery failed: SCL is held LOW on the bus"); recovery_result_ = RECOVERY_FAILED_SCL_LOW; return; } diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py index 0d413adb8a..ef95fd0b41 100644 --- a/esphome/components/i2s_audio/__init__.py +++ b/esphome/components/i2s_audio/__init__.py @@ -4,6 +4,7 @@ from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32.const import ( VARIANT_ESP32, VARIANT_ESP32C3, + VARIANT_ESP32P4, VARIANT_ESP32S2, VARIANT_ESP32S3, ) @@ -74,6 +75,7 @@ I2S_PORTS = { VARIANT_ESP32S2: 1, VARIANT_ESP32S3: 2, VARIANT_ESP32C3: 1, + VARIANT_ESP32P4: 3, } i2s_channel_fmt_t = cg.global_ns.enum("i2s_channel_fmt_t") diff --git a/esphome/components/i2s_audio/i2s_audio.cpp b/esphome/components/i2s_audio/i2s_audio.cpp index ad73b383fe..0f2995b4bd 100644 --- a/esphome/components/i2s_audio/i2s_audio.cpp +++ b/esphome/components/i2s_audio/i2s_audio.cpp @@ -14,18 +14,17 @@ static const uint8_t I2S_NUM_MAX = SOC_I2S_NUM; // because IDF 5+ took this awa #endif void I2SAudioComponent::setup() { - static i2s_port_t next_port_num = I2S_NUM_0; + ESP_LOGCONFIG(TAG, "Running setup"); + static i2s_port_t next_port_num = I2S_NUM_0; if (next_port_num >= I2S_NUM_MAX) { - ESP_LOGE(TAG, "Too many I2S Audio components!"); + ESP_LOGE(TAG, "Too many components"); this->mark_failed(); return; } this->port_ = next_port_num; next_port_num = (i2s_port_t) (next_port_num + 1); - - ESP_LOGCONFIG(TAG, "Setting up I2S Audio..."); } } // namespace i2s_audio diff --git a/esphome/components/i2s_audio/i2s_audio.h b/esphome/components/i2s_audio/i2s_audio.h index e839bcd891..cfccf7e01f 100644 --- a/esphome/components/i2s_audio/i2s_audio.h +++ b/esphome/components/i2s_audio/i2s_audio.h @@ -3,8 +3,8 @@ #ifdef USE_ESP32 #include "esphome/core/component.h" -#include "esphome/core/helpers.h" #include "esphome/core/defines.h" +#include "esphome/core/helpers.h" #ifdef USE_I2S_LEGACY #include #else diff --git a/esphome/components/i2s_audio/media_player/__init__.py b/esphome/components/i2s_audio/media_player/__init__.py index 51001e9444..f7ef134803 100644 --- a/esphome/components/i2s_audio/media_player/__init__.py +++ b/esphome/components/i2s_audio/media_player/__init__.py @@ -116,5 +116,5 @@ async def to_code(config): cg.add_library("WiFiClientSecure", None) cg.add_library("HTTPClient", None) - cg.add_library("esphome/ESP32-audioI2S", "2.0.7") + cg.add_library("esphome/ESP32-audioI2S", "2.2.0") cg.add_build_flag("-DAUDIO_NO_SD_FS") diff --git a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp index 34ed5b02a0..57e184d7f8 100644 --- a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp +++ b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp @@ -120,7 +120,7 @@ void I2SAudioMediaPlayer::set_volume_(float volume, bool publish) { } void I2SAudioMediaPlayer::setup() { - ESP_LOGCONFIG(TAG, "Setting up Audio..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->state = media_player::MEDIA_PLAYER_STATE_IDLE; } @@ -244,8 +244,10 @@ void I2SAudioMediaPlayer::dump_config() { } } else { #endif - ESP_LOGCONFIG(TAG, " External DAC channels: %d", this->external_dac_channels_); - ESP_LOGCONFIG(TAG, " I2S DOUT Pin: %d", this->dout_pin_); + ESP_LOGCONFIG(TAG, + " External DAC channels: %d\n" + " I2S DOUT Pin: %d", + this->external_dac_channels_, this->dout_pin_); LOG_PIN(" Mute Pin: ", this->mute_pin_); #if SOC_I2S_SUPPORTS_DAC } diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index 7c11d4f47d..0477e0682d 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -40,12 +40,12 @@ enum MicrophoneEventGroupBits : uint32_t { }; void I2SAudioMicrophone::setup() { - ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone..."); + ESP_LOGCONFIG(TAG, "Running setup"); #ifdef USE_I2S_LEGACY #if SOC_I2S_SUPPORTS_ADC if (this->adc_) { if (this->parent_->get_port() != I2S_NUM_0) { - ESP_LOGE(TAG, "Internal ADC only works on I2S0!"); + ESP_LOGE(TAG, "Internal ADC only works on I2S0"); this->mark_failed(); return; } @@ -55,7 +55,7 @@ void I2SAudioMicrophone::setup() { { if (this->pdm_) { if (this->parent_->get_port() != I2S_NUM_0) { - ESP_LOGE(TAG, "PDM only works on I2S0!"); + ESP_LOGE(TAG, "PDM only works on I2S0"); this->mark_failed(); return; } @@ -64,14 +64,14 @@ void I2SAudioMicrophone::setup() { this->active_listeners_semaphore_ = xSemaphoreCreateCounting(MAX_LISTENERS, MAX_LISTENERS); if (this->active_listeners_semaphore_ == nullptr) { - ESP_LOGE(TAG, "Failed to create semaphore"); + ESP_LOGE(TAG, "Creating semaphore failed"); this->mark_failed(); return; } this->event_group_ = xEventGroupCreate(); if (this->event_group_ == nullptr) { - ESP_LOGE(TAG, "Failed to create event group"); + ESP_LOGE(TAG, "Creating event group failed"); this->mark_failed(); return; } @@ -79,6 +79,15 @@ void I2SAudioMicrophone::setup() { this->configure_stream_settings_(); } +void I2SAudioMicrophone::dump_config() { + ESP_LOGCONFIG(TAG, + "Microphone:\n" + " Pin: %d\n" + " PDM: %s\n" + " DC offset correction: %s", + static_cast(this->din_pin_), YESNO(this->pdm_), YESNO(this->correct_dc_offset_)); +} + void I2SAudioMicrophone::configure_stream_settings_() { uint8_t channel_count = 1; #ifdef USE_I2S_LEGACY @@ -127,6 +136,7 @@ bool I2SAudioMicrophone::start_driver_() { if (!this->parent_->try_lock()) { return false; // Waiting for another i2s to return lock } + this->locked_driver_ = true; esp_err_t err; #ifdef USE_I2S_LEGACY @@ -151,7 +161,7 @@ bool I2SAudioMicrophone::start_driver_() { config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN); err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr); if (err != ESP_OK) { - ESP_LOGE(TAG, "Error installing I2S driver: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error installing driver: %s", esp_err_to_name(err)); return false; } @@ -174,7 +184,7 @@ bool I2SAudioMicrophone::start_driver_() { err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr); if (err != ESP_OK) { - ESP_LOGE(TAG, "Error installing I2S driver: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error installing driver: %s", esp_err_to_name(err)); return false; } @@ -183,7 +193,7 @@ bool I2SAudioMicrophone::start_driver_() { err = i2s_set_pin(this->parent_->get_port(), &pin_config); if (err != ESP_OK) { - ESP_LOGE(TAG, "Error setting I2S pin: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error setting pin: %s", esp_err_to_name(err)); return false; } } @@ -198,7 +208,7 @@ bool I2SAudioMicrophone::start_driver_() { /* Allocate a new RX channel and get the handle of this channel */ err = i2s_new_channel(&chan_cfg, NULL, &this->rx_handle_); if (err != ESP_OK) { - ESP_LOGE(TAG, "Error creating new I2S channel: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error creating channel: %s", esp_err_to_name(err)); return false; } @@ -270,14 +280,14 @@ bool I2SAudioMicrophone::start_driver_() { err = i2s_channel_init_std_mode(this->rx_handle_, &std_cfg); } if (err != ESP_OK) { - ESP_LOGE(TAG, "Error initializing I2S channel: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error initializing channel: %s", esp_err_to_name(err)); return false; } /* Before reading data, start the RX channel first */ i2s_channel_enable(this->rx_handle_); if (err != ESP_OK) { - ESP_LOGE(TAG, "Error enabling I2S Microphone: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Enabling failed: %s", esp_err_to_name(err)); return false; } #endif @@ -304,31 +314,37 @@ void I2SAudioMicrophone::stop_driver_() { if (this->adc_) { err = i2s_adc_disable(this->parent_->get_port()); if (err != ESP_OK) { - ESP_LOGW(TAG, "Error disabling ADC - it may not have started: %s", esp_err_to_name(err)); + ESP_LOGW(TAG, "Error disabling ADC: %s", esp_err_to_name(err)); } } #endif err = i2s_stop(this->parent_->get_port()); if (err != ESP_OK) { - ESP_LOGW(TAG, "Error stopping I2S microphone - it may not have started: %s", esp_err_to_name(err)); + ESP_LOGW(TAG, "Error stopping: %s", esp_err_to_name(err)); } err = i2s_driver_uninstall(this->parent_->get_port()); if (err != ESP_OK) { - ESP_LOGW(TAG, "Error uninstalling I2S driver - it may not have started: %s", esp_err_to_name(err)); + ESP_LOGW(TAG, "Error uninstalling driver: %s", esp_err_to_name(err)); } #else - /* Have to stop the channel before deleting it */ - err = i2s_channel_disable(this->rx_handle_); - if (err != ESP_OK) { - ESP_LOGW(TAG, "Error stopping I2S microphone - it may not have started: %s", esp_err_to_name(err)); - } - /* If the handle is not needed any more, delete it to release the channel resources */ - err = i2s_del_channel(this->rx_handle_); - if (err != ESP_OK) { - ESP_LOGW(TAG, "Error deleting I2S channel - it may not have started: %s", esp_err_to_name(err)); + if (this->rx_handle_ != nullptr) { + /* Have to stop the channel before deleting it */ + err = i2s_channel_disable(this->rx_handle_); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Error stopping: %s", esp_err_to_name(err)); + } + /* If the handle is not needed any more, delete it to release the channel resources */ + err = i2s_del_channel(this->rx_handle_); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Error deleting channel: %s", esp_err_to_name(err)); + } + this->rx_handle_ = nullptr; } #endif - this->parent_->unlock(); + if (this->locked_driver_) { + this->parent_->unlock(); + this->locked_driver_ = false; + } } void I2SAudioMicrophone::mic_task(void *params) { @@ -400,7 +416,7 @@ size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_w // Ignore ESP_ERR_TIMEOUT if ticks_to_wait = 0, as it will read the data on the next call if (!this->status_has_warning()) { // Avoid spamming the logs with the error message if its repeated - ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err)); + ESP_LOGW(TAG, "Read error: %s", esp_err_to_name(err)); } this->status_set_warning(); return 0; @@ -428,19 +444,19 @@ void I2SAudioMicrophone::loop() { uint32_t event_group_bits = xEventGroupGetBits(this->event_group_); if (event_group_bits & MicrophoneEventGroupBits::TASK_STARTING) { - ESP_LOGD(TAG, "Task started, attempting to allocate buffer"); + ESP_LOGV(TAG, "Task started, attempting to allocate buffer"); xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STARTING); } if (event_group_bits & MicrophoneEventGroupBits::TASK_RUNNING) { - ESP_LOGD(TAG, "Task is running and reading data"); + ESP_LOGV(TAG, "Task is running and reading data"); xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_RUNNING); this->state_ = microphone::STATE_RUNNING; } if ((event_group_bits & MicrophoneEventGroupBits::TASK_STOPPED)) { - ESP_LOGD(TAG, "Task finished, freeing resources and uninstalling I2S driver"); + ESP_LOGV(TAG, "Task finished, freeing resources and uninstalling driver"); vTaskDelete(this->task_handle_); this->task_handle_ = nullptr; @@ -470,7 +486,8 @@ void I2SAudioMicrophone::loop() { } if (!this->start_driver_()) { - this->status_momentary_error("I2S driver failed to start, unloading it and attempting again in 1 second", 1000); + ESP_LOGE(TAG, "Driver failed to start; retrying in 1 second"); + this->status_momentary_error("driver_fail", 1000); this->stop_driver_(); // Stop/frees whatever possibly started break; } @@ -480,7 +497,8 @@ void I2SAudioMicrophone::loop() { &this->task_handle_); if (this->task_handle_ == nullptr) { - this->status_momentary_error("Task failed to start, attempting again in 1 second", 1000); + ESP_LOGE(TAG, "Task failed to start, retrying in 1 second"); + this->status_momentary_error("task_fail", 1000); this->stop_driver_(); // Stops the driver to return the lock; will be reloaded in next attempt } } diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h index c35f88f8ee..5f66f2e962 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h @@ -18,6 +18,7 @@ namespace i2s_audio { class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, public Component { public: void setup() override; + void dump_config() override; void start() override; void stop() override; @@ -80,6 +81,7 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub bool pdm_{false}; bool correct_dc_offset_; + bool locked_driver_{false}; int32_t dc_offset_{0}; }; diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index d85409f1a8..41da8a4642 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -99,7 +99,7 @@ static const std::vector Q15_VOLUME_SCALING_FACTORS = { 19508, 20665, 21891, 23189, 24565, 26022, 27566, 29201, 30933, 32767}; void I2SAudioSpeaker::setup() { - ESP_LOGCONFIG(TAG, "Setting up I2S Audio Speaker..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->event_group_ = xEventGroupCreate(); @@ -110,29 +110,48 @@ void I2SAudioSpeaker::setup() { } } +void I2SAudioSpeaker::dump_config() { + ESP_LOGCONFIG(TAG, + "Speaker:\n" + " Pin: %d\n" + " Buffer duration: %" PRIu32, + static_cast(this->dout_pin_), this->buffer_duration_ms_); + if (this->timeout_.has_value()) { + ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 " ms", this->timeout_.value()); + } +#ifdef USE_I2S_LEGACY +#if SOC_I2S_SUPPORTS_DAC + ESP_LOGCONFIG(TAG, " Internal DAC mode: %d", static_cast(this->internal_dac_mode_)); +#endif + ESP_LOGCONFIG(TAG, " Communication format: %d", static_cast(this->i2s_comm_fmt_)); +#else + ESP_LOGCONFIG(TAG, " Communication format: %s", this->i2s_comm_fmt_.c_str()); +#endif +} + void I2SAudioSpeaker::loop() { uint32_t event_group_bits = xEventGroupGetBits(this->event_group_); if (event_group_bits & SpeakerEventGroupBits::STATE_STARTING) { - ESP_LOGD(TAG, "Starting Speaker"); + ESP_LOGD(TAG, "Starting"); this->state_ = speaker::STATE_STARTING; xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STARTING); } if (event_group_bits & SpeakerEventGroupBits::STATE_RUNNING) { - ESP_LOGD(TAG, "Started Speaker"); + ESP_LOGD(TAG, "Started"); this->state_ = speaker::STATE_RUNNING; xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_RUNNING); this->status_clear_warning(); this->status_clear_error(); } if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPING) { - ESP_LOGD(TAG, "Stopping Speaker"); + ESP_LOGD(TAG, "Stopping"); this->state_ = speaker::STATE_STOPPING; xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPING); } if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPED) { if (!this->task_created_) { - ESP_LOGD(TAG, "Stopped Speaker"); + ESP_LOGD(TAG, "Stopped"); this->state_ = speaker::STATE_STOPPED; xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS); this->speaker_task_handle_ = nullptr; @@ -140,20 +159,19 @@ void I2SAudioSpeaker::loop() { } if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) { - this->status_set_error("Failed to start speaker task"); + this->status_set_error("Failed to start task"); xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START); } if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) { uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS; - ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits))); + ESP_LOGW(TAG, "Writing failed: %s", esp_err_to_name(err_bit_to_esp_err(error_bits))); this->status_set_warning(); } if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED) { - this->status_set_error("Failed to adjust I2S bus to match the incoming audio"); - ESP_LOGE(TAG, - "Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8, + this->status_set_error("Failed to adjust bus to match incoming audio"); + ESP_LOGE(TAG, "Incompatible audio format: sample rate = %" PRIu32 ", channels = %u, bits per sample = %u", this->audio_stream_info_.get_sample_rate(), this->audio_stream_info_.get_channels(), this->audio_stream_info_.get_bits_per_sample()); } @@ -202,7 +220,7 @@ void I2SAudioSpeaker::set_mute_state(bool mute_state) { size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) { if (this->is_failed()) { - ESP_LOGE(TAG, "Cannot play audio, speaker failed to setup"); + ESP_LOGE(TAG, "Setup failed; cannot play audio"); return 0; } if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) { diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index b5e4b94bc4..eb2a0ae756 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -24,6 +24,7 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp float get_setup_priority() const override { return esphome::setup_priority::PROCESSOR; } void setup() override; + void dump_config() override; void loop() override; void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; } diff --git a/esphome/components/iaqcore/iaqcore.cpp b/esphome/components/iaqcore/iaqcore.cpp index 810e8da0b2..2a84eabf75 100644 --- a/esphome/components/iaqcore/iaqcore.cpp +++ b/esphome/components/iaqcore/iaqcore.cpp @@ -1,7 +1,7 @@ #include "iaqcore.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace iaqcore { @@ -26,7 +26,7 @@ struct SensorData { void IAQCore::setup() { if (this->write(nullptr, 0) != i2c::ERROR_OK) { - ESP_LOGD(TAG, "Communication failed!"); + ESP_LOGD(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); return; } @@ -89,7 +89,7 @@ void IAQCore::dump_config() { LOG_I2C_DEVICE(this); LOG_UPDATE_INTERVAL(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with AMS iAQ Core failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_SENSOR(" ", "CO2", this->co2_); LOG_SENSOR(" ", "TVOC", this->tvoc_); diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index f056f0a128..41fd89cc58 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -89,8 +89,10 @@ void ILI9XXXDisplay::setup_pins_() { void ILI9XXXDisplay::dump_config() { LOG_DISPLAY("", "ili9xxx", this); - ESP_LOGCONFIG(TAG, " Width Offset: %u", this->offset_x_); - ESP_LOGCONFIG(TAG, " Height Offset: %u", this->offset_y_); + ESP_LOGCONFIG(TAG, + " Width Offset: %u\n" + " Height Offset: %u", + this->offset_x_, this->offset_y_); switch (this->buffer_color_mode_) { case BITS_8_INDEXED: ESP_LOGCONFIG(TAG, " Color mode: 8bit Indexed"); @@ -111,11 +113,14 @@ void ILI9XXXDisplay::dump_config() { LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Busy Pin: ", this->busy_pin_); - ESP_LOGCONFIG(TAG, " Color order: %s", this->color_order_ == display::COLOR_ORDER_BGR ? "BGR" : "RGB"); - ESP_LOGCONFIG(TAG, " Swap_xy: %s", YESNO(this->swap_xy_)); - ESP_LOGCONFIG(TAG, " Mirror_x: %s", YESNO(this->mirror_x_)); - ESP_LOGCONFIG(TAG, " Mirror_y: %s", YESNO(this->mirror_y_)); - ESP_LOGCONFIG(TAG, " Invert colors: %s", YESNO(this->pre_invertcolors_)); + ESP_LOGCONFIG(TAG, + " Color order: %s\n" + " Swap_xy: %s\n" + " Mirror_x: %s\n" + " Mirror_y: %s\n" + " Invert colors: %s", + this->color_order_ == display::COLOR_ORDER_BGR ? "BGR" : "RGB", YESNO(this->swap_xy_), + YESNO(this->mirror_x_), YESNO(this->mirror_y_), YESNO(this->pre_invertcolors_)); if (this->is_failed()) { ESP_LOGCONFIG(TAG, " => Failed to init Memory: YES!"); diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index 0babcedc48..629bbb41cb 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -89,7 +89,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer, void dump_config() override; void setup() override; - void on_shutdown() override { this->command(ILI9XXX_SLPIN); } + void on_powerdown() override { this->command(ILI9XXX_SLPIN); } display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, diff --git a/esphome/components/image/image.cpp b/esphome/components/image/image.cpp index 82e46e3460..7b65c4d0cb 100644 --- a/esphome/components/image/image.cpp +++ b/esphome/components/image/image.cpp @@ -1,6 +1,7 @@ #include "image.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" namespace esphome { namespace image { diff --git a/esphome/components/ina219/ina219.cpp b/esphome/components/ina219/ina219.cpp index 2fb3bea356..8d5271fa84 100644 --- a/esphome/components/ina219/ina219.cpp +++ b/esphome/components/ina219/ina219.cpp @@ -34,7 +34,7 @@ static const uint8_t INA219_REGISTER_CURRENT = 0x04; static const uint8_t INA219_REGISTER_CALIBRATION = 0x05; void INA219Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up INA219..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Config Register // 0bx000000000000000 << 15 RESET Bit (1 -> trigger reset) if (!this->write_byte_16(INA219_REGISTER_CONFIG, 0x8000)) { @@ -134,7 +134,7 @@ void INA219Component::dump_config() { LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with INA219 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); return; } LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/ina226/ina226.cpp b/esphome/components/ina226/ina226.cpp index 7a57c118af..52e7127708 100644 --- a/esphome/components/ina226/ina226.cpp +++ b/esphome/components/ina226/ina226.cpp @@ -37,7 +37,7 @@ static const uint16_t INA226_ADC_TIMES[] = {140, 204, 332, 588, 1100, 2116, 4156 static const uint16_t INA226_ADC_AVG_SAMPLES[] = {1, 4, 16, 64, 128, 256, 512, 1024}; void INA226Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up INA226..."); + ESP_LOGCONFIG(TAG, "Running setup"); ConfigurationRegister config; @@ -88,14 +88,17 @@ void INA226Component::dump_config() { LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with INA226 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); return; } LOG_UPDATE_INTERVAL(this); - ESP_LOGCONFIG(TAG, " ADC Conversion Time Bus Voltage: %d", INA226_ADC_TIMES[this->adc_time_voltage_ & 0b111]); - ESP_LOGCONFIG(TAG, " ADC Conversion Time Shunt Voltage: %d", INA226_ADC_TIMES[this->adc_time_current_ & 0b111]); - ESP_LOGCONFIG(TAG, " ADC Averaging Samples: %d", INA226_ADC_AVG_SAMPLES[this->adc_avg_samples_ & 0b111]); + ESP_LOGCONFIG(TAG, + " ADC Conversion Time Bus Voltage: %d\n" + " ADC Conversion Time Shunt Voltage: %d\n" + " ADC Averaging Samples: %d", + INA226_ADC_TIMES[this->adc_time_voltage_ & 0b111], INA226_ADC_TIMES[this->adc_time_current_ & 0b111], + INA226_ADC_AVG_SAMPLES[this->adc_avg_samples_ & 0b111]); LOG_SENSOR(" ", "Bus Voltage", this->bus_voltage_sensor_); LOG_SENSOR(" ", "Shunt Voltage", this->shunt_voltage_sensor_); diff --git a/esphome/components/ina260/ina260.cpp b/esphome/components/ina260/ina260.cpp index 2f220e6a11..2b6208f60f 100644 --- a/esphome/components/ina260/ina260.cpp +++ b/esphome/components/ina260/ina260.cpp @@ -35,7 +35,7 @@ static const uint8_t INA260_REGISTER_MANUFACTURE_ID = 0xFE; static const uint8_t INA260_REGISTER_DEVICE_ID = 0xFF; void INA260Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up INA260..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Reset device on setup if (!this->write_byte_16(INA260_REGISTER_CONFIG, 0x8000)) { diff --git a/esphome/components/ina2xx_base/ina2xx_base.cpp b/esphome/components/ina2xx_base/ina2xx_base.cpp index 924bf91e5e..2112a28b02 100644 --- a/esphome/components/ina2xx_base/ina2xx_base.cpp +++ b/esphome/components/ina2xx_base/ina2xx_base.cpp @@ -1,7 +1,7 @@ #include "ina2xx_base.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include #include @@ -50,7 +50,7 @@ static bool check_model_and_device_match(INAModel model, uint16_t dev_id) { } void INA2XX::setup() { - ESP_LOGCONFIG(TAG, "Setting up INA2xx..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->reset_config_()) { ESP_LOGE(TAG, "Reset failed, check connection"); @@ -203,15 +203,19 @@ void INA2XX::dump_config() { this->dev_id_); } if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with INA2xx failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); - ESP_LOGCONFIG(TAG, " Shunt resistance = %f Ohm", this->shunt_resistance_ohm_); - ESP_LOGCONFIG(TAG, " Max current = %f A", this->max_current_a_); - ESP_LOGCONFIG(TAG, " Shunt temp coeff = %d ppm/°C", this->shunt_tempco_ppm_c_); - ESP_LOGCONFIG(TAG, " ADCRANGE = %d (%s)", (uint8_t) this->adc_range_, this->adc_range_ ? "±40.96 mV" : "±163.84 mV"); - ESP_LOGCONFIG(TAG, " CURRENT_LSB = %f", this->current_lsb_); - ESP_LOGCONFIG(TAG, " SHUNT_CAL = %d", this->shunt_cal_); + ESP_LOGCONFIG(TAG, + " Shunt resistance = %f Ohm\n" + " Max current = %f A\n" + " Shunt temp coeff = %d ppm/°C\n" + " ADCRANGE = %d (%s)\n" + " CURRENT_LSB = %f\n" + " SHUNT_CAL = %d", + this->shunt_resistance_ohm_, this->max_current_a_, this->shunt_tempco_ppm_c_, + (uint8_t) this->adc_range_, this->adc_range_ ? "±40.96 mV" : "±163.84 mV", this->current_lsb_, + this->shunt_cal_); ESP_LOGCONFIG(TAG, " ADC Samples = %d; ADC times: Bus = %d μs, Shunt = %d μs, Temp = %d μs", ADC_SAMPLES[0b111 & (uint8_t) this->adc_avg_samples_], diff --git a/esphome/components/ina3221/ina3221.cpp b/esphome/components/ina3221/ina3221.cpp index 3f8e2d06df..35e79462ab 100644 --- a/esphome/components/ina3221/ina3221.cpp +++ b/esphome/components/ina3221/ina3221.cpp @@ -22,7 +22,7 @@ static const uint8_t INA3221_REGISTER_CHANNEL3_BUS_VOLTAGE = 0x06; // A0 = SCL -> 0x43 void INA3221Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up INA3221..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Config Register // 0bx000000000000000 << 15 RESET Bit (1 -> trigger reset) if (!this->write_byte_16(INA3221_REGISTER_CONFIG, 0x8000)) { @@ -60,7 +60,7 @@ void INA3221Component::dump_config() { ESP_LOGCONFIG(TAG, "INA3221:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with INA3221 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/inkplate6/display.py b/esphome/components/inkplate6/display.py index 56d20508ec..a7d31c0131 100644 --- a/esphome/components/inkplate6/display.py +++ b/esphome/components/inkplate6/display.py @@ -6,9 +6,12 @@ from esphome.const import ( CONF_FULL_UPDATE_EVERY, CONF_ID, CONF_LAMBDA, + CONF_MIRROR_X, + CONF_MIRROR_Y, CONF_MODEL, CONF_OE_PIN, CONF_PAGES, + CONF_TRANSFORM, CONF_WAKEUP_PIN, ) @@ -36,7 +39,6 @@ CONF_SPH_PIN = "sph_pin" CONF_SPV_PIN = "spv_pin" CONF_VCOM_PIN = "vcom_pin" - inkplate6_ns = cg.esphome_ns.namespace("inkplate6") Inkplate6 = inkplate6_ns.class_( "Inkplate6", @@ -62,6 +64,12 @@ CONFIG_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(Inkplate6), cv.Optional(CONF_GREYSCALE, default=False): cv.boolean, + cv.Optional(CONF_TRANSFORM): cv.Schema( + { + cv.Optional(CONF_MIRROR_X, default=False): cv.boolean, + cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean, + } + ), cv.Optional(CONF_PARTIAL_UPDATING, default=True): cv.boolean, cv.Optional(CONF_FULL_UPDATE_EVERY, default=10): cv.uint32_t, cv.Optional(CONF_MODEL, default="inkplate_6"): cv.enum( @@ -109,7 +117,6 @@ CONFIG_SCHEMA = cv.All( .extend(cv.polling_component_schema("5s")) .extend(i2c.i2c_device_schema(0x48)), cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), - cv.only_with_arduino, ) @@ -126,6 +133,9 @@ async def to_code(config): cg.add(var.set_writer(lambda_)) cg.add(var.set_greyscale(config[CONF_GREYSCALE])) + if transform := config.get(CONF_TRANSFORM): + cg.add(var.set_mirror_x(transform[CONF_MIRROR_X])) + cg.add(var.set_mirror_y(transform[CONF_MIRROR_Y])) cg.add(var.set_partial_updating(config[CONF_PARTIAL_UPDATING])) cg.add(var.set_full_update_every(config[CONF_FULL_UPDATE_EVERY])) diff --git a/esphome/components/inkplate6/inkplate.cpp b/esphome/components/inkplate6/inkplate.cpp index f4d0fedf83..247aa35ead 100644 --- a/esphome/components/inkplate6/inkplate.cpp +++ b/esphome/components/inkplate6/inkplate.cpp @@ -1,11 +1,9 @@ #include "inkplate.h" -#include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" -#ifdef USE_ESP32_FRAMEWORK_ARDUINO - -#include +#include namespace esphome { namespace inkplate6 { @@ -156,6 +154,12 @@ void HOT Inkplate6::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0) return; + if (this->mirror_y_) + y = this->get_height_internal() - y - 1; + + if (this->mirror_x_) + x = this->get_width_internal() - x - 1; + if (this->greyscale_) { int x1 = x / 2; int x_sub = x % 2; @@ -180,9 +184,11 @@ void HOT Inkplate6::draw_absolute_pixel_internal(int x, int y, Color color) { void Inkplate6::dump_config() { LOG_DISPLAY("", "Inkplate", this); - ESP_LOGCONFIG(TAG, " Greyscale: %s", YESNO(this->greyscale_)); - ESP_LOGCONFIG(TAG, " Partial Updating: %s", YESNO(this->partial_updating_)); - ESP_LOGCONFIG(TAG, " Full Update Every: %d", this->full_update_every_); + ESP_LOGCONFIG(TAG, + " Greyscale: %s\n" + " Partial Updating: %s\n" + " Full Update Every: %d", + YESNO(this->greyscale_), YESNO(this->partial_updating_), this->full_update_every_); // Log pins LOG_PIN(" CKV Pin: ", this->ckv_pin_); LOG_PIN(" CL Pin: ", this->cl_pin_); @@ -715,5 +721,3 @@ void Inkplate6::pins_as_outputs_() { } // namespace inkplate6 } // namespace esphome - -#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/inkplate6/inkplate.h b/esphome/components/inkplate6/inkplate.h index ca2ad46f1e..d8918bdf2a 100644 --- a/esphome/components/inkplate6/inkplate.h +++ b/esphome/components/inkplate6/inkplate.h @@ -1,11 +1,9 @@ #pragma once +#include "esphome/components/display/display_buffer.h" +#include "esphome/components/i2c/i2c.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" -#include "esphome/components/i2c/i2c.h" -#include "esphome/components/display/display_buffer.h" - -#ifdef USE_ESP32_FRAMEWORK_ARDUINO namespace esphome { namespace inkplate6 { @@ -92,6 +90,9 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice { if (this->is_ready()) this->initialize_(); } + void set_mirror_y(bool mirror_y) { this->mirror_y_ = mirror_y; } + void set_mirror_x(bool mirror_x) { this->mirror_x_ = mirror_x; } + void set_partial_updating(bool partial_updating) { this->partial_updating_ = partial_updating; } void set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; } @@ -221,6 +222,8 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice { bool block_partial_{true}; bool greyscale_; + bool mirror_y_{false}; + bool mirror_x_{false}; bool partial_updating_; InkplateModel model_; @@ -249,5 +252,3 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice { } // namespace inkplate6 } // namespace esphome - -#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/integration/integration_sensor.cpp b/esphome/components/integration/integration_sensor.cpp index 2ac7caca21..c09778e79e 100644 --- a/esphome/components/integration/integration_sensor.cpp +++ b/esphome/components/integration/integration_sensor.cpp @@ -1,7 +1,7 @@ #include "integration_sensor.h" +#include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/hal.h" namespace esphome { namespace integration { diff --git a/esphome/components/internal_temperature/internal_temperature.cpp b/esphome/components/internal_temperature/internal_temperature.cpp index d3ff7433b6..f503927d8e 100644 --- a/esphome/components/internal_temperature/internal_temperature.cpp +++ b/esphome/components/internal_temperature/internal_temperature.cpp @@ -66,7 +66,7 @@ void InternalTemperatureSensor::update() { esp_err_t result = temperature_sensor_get_celsius(tsensNew, &temperature); success = (result == ESP_OK); if (!success) { - ESP_LOGE(TAG, "Failed to get temperature: %d", result); + ESP_LOGE(TAG, "Reading failed (%d)", result); } #endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) #endif // USE_ESP32_VARIANT @@ -103,20 +103,20 @@ void InternalTemperatureSensor::setup() { (defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S2) || \ defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32C2) || \ defined(USE_ESP32_VARIANT_ESP32P4)) - ESP_LOGCONFIG(TAG, "Setting up temperature sensor..."); + ESP_LOGCONFIG(TAG, "Running setup"); temperature_sensor_config_t tsens_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80); esp_err_t result = temperature_sensor_install(&tsens_config, &tsensNew); if (result != ESP_OK) { - ESP_LOGE(TAG, "Failed to install temperature sensor: %d", result); + ESP_LOGE(TAG, "Install failed (%d)", result); this->mark_failed(); return; } result = temperature_sensor_enable(tsensNew); if (result != ESP_OK) { - ESP_LOGE(TAG, "Failed to enable temperature sensor: %d", result); + ESP_LOGE(TAG, "Enabling failed (%d)", result); this->mark_failed(); return; } diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index d50b2b483c..6c66476dc1 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -20,8 +20,7 @@ std::string build_json(const json_build_t &f) { ESP_LOGV(TAG, "Attempting to allocate %zu bytes for JSON serialization", request_size); DynamicJsonDocument json_document(request_size); if (json_document.capacity() == 0) { - ESP_LOGE(TAG, - "Could not allocate memory for JSON document! Requested %zu bytes, largest free heap block: %zu bytes", + ESP_LOGE(TAG, "Could not allocate memory for document! Requested %zu bytes, largest free heap block: %zu bytes", request_size, free_heap); return "{}"; } @@ -29,7 +28,7 @@ std::string build_json(const json_build_t &f) { f(root); if (json_document.overflowed()) { if (request_size == free_heap) { - ESP_LOGE(TAG, "Could not allocate memory for JSON document! Overflowed largest free heap block: %zu bytes", + ESP_LOGE(TAG, "Could not allocate memory for document! Overflowed largest free heap block: %zu bytes", free_heap); return "{}"; } @@ -54,7 +53,7 @@ bool parse_json(const std::string &data, const json_parse_t &f) { while (true) { DynamicJsonDocument json_document(request_size); if (json_document.capacity() == 0) { - ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %zu bytes, free heap: %zu", request_size, + ESP_LOGE(TAG, "Could not allocate memory for document! Requested %zu bytes, free heap: %zu", request_size, free_heap); return false; } @@ -74,7 +73,7 @@ bool parse_json(const std::string &data, const json_parse_t &f) { request_size *= 2; continue; } else { - ESP_LOGE(TAG, "JSON parse error: %s", err.c_str()); + ESP_LOGE(TAG, "Parse error: %s", err.c_str()); return false; } }; diff --git a/esphome/components/kamstrup_kmp/kamstrup_kmp.cpp b/esphome/components/kamstrup_kmp/kamstrup_kmp.cpp index b870d1b56d..c058c7b3aa 100644 --- a/esphome/components/kamstrup_kmp/kamstrup_kmp.cpp +++ b/esphome/components/kamstrup_kmp/kamstrup_kmp.cpp @@ -10,7 +10,7 @@ static const char *const TAG = "kamstrup_kmp"; void KamstrupKMPComponent::dump_config() { ESP_LOGCONFIG(TAG, "kamstrup_kmp:"); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with Kamstrup meter failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/key_collector/key_collector.cpp b/esphome/components/key_collector/key_collector.cpp index ffb4b47fa2..9cfc74f50e 100644 --- a/esphome/components/key_collector/key_collector.cpp +++ b/esphome/components/key_collector/key_collector.cpp @@ -32,8 +32,10 @@ void KeyCollector::dump_config() { if (!this->start_keys_.empty()) ESP_LOGCONFIG(TAG, " start keys '%s'", this->start_keys_.c_str()); if (!this->end_keys_.empty()) { - ESP_LOGCONFIG(TAG, " end keys '%s'", this->end_keys_.c_str()); - ESP_LOGCONFIG(TAG, " end key is required: %s", ONOFF(this->end_key_required_)); + ESP_LOGCONFIG(TAG, + " end keys '%s'\n" + " end key is required: %s", + this->end_keys_.c_str(), ONOFF(this->end_key_required_)); } if (!this->allowed_keys_.empty()) ESP_LOGCONFIG(TAG, " allowed keys '%s'", this->allowed_keys_.c_str()); diff --git a/esphome/components/kmeteriso/kmeteriso.cpp b/esphome/components/kmeteriso/kmeteriso.cpp index 0276ab3f67..714df0b538 100644 --- a/esphome/components/kmeteriso/kmeteriso.cpp +++ b/esphome/components/kmeteriso/kmeteriso.cpp @@ -1,5 +1,6 @@ #include "kmeteriso.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -13,14 +14,13 @@ static const uint8_t KMETER_INTERNAL_TEMP_VAL_REG = 0x10; static const uint8_t KMETER_FIRMWARE_VERSION_REG = 0xFE; void KMeterISOComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up KMeterISO..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->error_code_ = NONE; // Mark as not failed before initializing. Some devices will turn off sensors to save on batteries // and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component. - if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) { - this->component_state_ &= ~COMPONENT_STATE_MASK; - this->component_state_ |= COMPONENT_STATE_CONSTRUCTION; + if (this->is_failed()) { + this->reset_to_construction_state(); } auto err = this->bus_->writev(this->address_, nullptr, 0); diff --git a/esphome/components/kuntze/kuntze.cpp b/esphome/components/kuntze/kuntze.cpp index 8ab7af8cd9..42545d9d54 100644 --- a/esphome/components/kuntze/kuntze.cpp +++ b/esphome/components/kuntze/kuntze.cpp @@ -77,8 +77,10 @@ void Kuntze::loop() { void Kuntze::update() { this->state_ = 1; } void Kuntze::dump_config() { - ESP_LOGCONFIG(TAG, "Kuntze:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "Kuntze:\n" + " Address: 0x%02X", + this->address_); LOG_SENSOR("", "pH", this->ph_sensor_); LOG_SENSOR("", "temperature", this->temperature_sensor_); LOG_SENSOR("", "DIS1", this->dis1_sensor_); diff --git a/esphome/components/lc709203f/__init__.py b/esphome/components/lc709203f/__init__.py new file mode 100644 index 0000000000..3be68d174f --- /dev/null +++ b/esphome/components/lc709203f/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@ilikecake"] diff --git a/esphome/components/lc709203f/lc709203f.cpp b/esphome/components/lc709203f/lc709203f.cpp new file mode 100644 index 0000000000..d95a2c1d5e --- /dev/null +++ b/esphome/components/lc709203f/lc709203f.cpp @@ -0,0 +1,299 @@ +#include "esphome/core/log.h" +#include "lc709203f.h" + +namespace esphome { +namespace lc709203f { + +static const char *const TAG = "lc709203f.sensor"; + +// Device I2C address. This address is fixed. +static const uint8_t LC709203F_I2C_ADDR_DEFAULT = 0x0B; + +// Device registers +static const uint8_t LC709203F_BEFORE_RSOC = 0x04; +static const uint8_t LC709203F_THERMISTOR_B = 0x06; +static const uint8_t LC709203F_INITIAL_RSOC = 0x07; +static const uint8_t LC709203F_CELL_TEMPERATURE = 0x08; +static const uint8_t LC709203F_CELL_VOLTAGE = 0x09; +static const uint8_t LC709203F_CURRENT_DIRECTION = 0x0A; +static const uint8_t LC709203F_APA = 0x0B; +static const uint8_t LC709203F_APT = 0x0C; +static const uint8_t LC709203F_RSOC = 0x0D; +static const uint8_t LC709203F_ITE = 0x0F; +static const uint8_t LC709203F_IC_VERSION = 0x11; +static const uint8_t LC709203F_CHANGE_OF_THE_PARAMETER = 0x12; +static const uint8_t LC709203F_ALARM_LOW_RSOC = 0x13; +static const uint8_t LC709203F_ALARM_LOW_CELL_VOLTAGE = 0x14; +static const uint8_t LC709203F_IC_POWER_MODE = 0x15; +static const uint8_t LC709203F_STATUS_BIT = 0x16; +static const uint8_t LC709203F_NUMBER_OF_THE_PARAMETER = 0x1A; + +static const uint8_t LC709203F_POWER_MODE_ON = 0x0001; +static const uint8_t LC709203F_POWER_MODE_SLEEP = 0x0002; + +// The number of times to retry an I2C transaction before giving up. In my experience, +// 10 is a good number here that will take care of most bus issues that require retry. +static const uint8_t LC709203F_I2C_RETRY_COUNT = 10; + +void Lc709203f::setup() { + // Note: The setup implements a small state machine. This is because we want to have + // delays before and after sending the RSOC command. The full init process should be: + // INIT->RSOC->TEMP_SETUP->NORMAL + // The setup() function will only perform the first part of the initialization process. + // Assuming no errors, the whole process should occur during the setup() function and + // the first two calls to update(). After that, the part should remain in normal mode + // until a device reset. + // + // This device can be picky about I2C communication and can error out occasionally. The + // get/set register functions impelment retry logic to retry the I2C transactions. The + // initialization code checks the return code from those functions. If they don't return + // NO_ERROR (0x00), that part of the initialization aborts and will be retried on the next + // call to update(). + ESP_LOGCONFIG(TAG, "Running setup"); + + // Set power mode to on. Note that, unlike some other similar devices, in sleep mode the IC + // does not record power usage. If there is significant power consumption during sleep mode, + // the pack RSOC will likely no longer be correct. Because of that, I do not implement + // sleep mode on this device. + + // Initialize device registers. If any of these fail, retry during the update() function. + if (this->set_register_(LC709203F_IC_POWER_MODE, LC709203F_POWER_MODE_ON) != i2c::NO_ERROR) { + return; + } + + if (this->set_register_(LC709203F_APA, this->apa_) != i2c::NO_ERROR) { + return; + } + + if (this->set_register_(LC709203F_CHANGE_OF_THE_PARAMETER, this->pack_voltage_) != i2c::NO_ERROR) { + return; + } + + this->state_ = STATE_RSOC; + // Note: Initialization continues in the update() function. +} + +void Lc709203f::update() { + uint16_t buffer; + + if (this->state_ == STATE_NORMAL) { + // Note: If we fail to read from the data registers, we do not report any sensor reading. + if (this->voltage_sensor_ != nullptr) { + if (this->get_register_(LC709203F_CELL_VOLTAGE, &buffer) == i2c::NO_ERROR) { + // Raw units are mV + this->voltage_sensor_->publish_state(static_cast(buffer) / 1000.0f); + this->status_clear_warning(); + } + } + if (this->battery_remaining_sensor_ != nullptr) { + if (this->get_register_(LC709203F_ITE, &buffer) == i2c::NO_ERROR) { + // Raw units are .1% + this->battery_remaining_sensor_->publish_state(static_cast(buffer) / 10.0f); + this->status_clear_warning(); + } + } + if (this->temperature_sensor_ != nullptr) { + // I can't test this with a real thermistor because I don't have a device with + // an attached thermistor. I have turned on the sensor and made sure that it + // sets up the registers properly. + if (this->get_register_(LC709203F_CELL_TEMPERATURE, &buffer) == i2c::NO_ERROR) { + // Raw units are .1 K + this->temperature_sensor_->publish_state((static_cast(buffer) / 10.0f) - 273.15f); + this->status_clear_warning(); + } + } + } else if (this->state_ == STATE_INIT) { + // Retry initializing the device registers. We should only get here if the init sequence + // failed during the setup() function. This would likely occur because of a repeated failures + // on the I2C bus. If any of these fail, retry the next time the update() function is called. + if (this->set_register_(LC709203F_IC_POWER_MODE, LC709203F_POWER_MODE_ON) != i2c::NO_ERROR) { + return; + } + + if (this->set_register_(LC709203F_APA, this->apa_) != i2c::NO_ERROR) { + return; + } + + if (this->set_register_(LC709203F_CHANGE_OF_THE_PARAMETER, this->pack_voltage_) != i2c::NO_ERROR) { + return; + } + + this->state_ = STATE_RSOC; + + } else if (this->state_ == STATE_RSOC) { + // We implement a delay here to send the initial RSOC command. + // This should run once on the first update() after initialization. + if (this->set_register_(LC709203F_INITIAL_RSOC, 0xAA55) == i2c::NO_ERROR) { + this->state_ = STATE_TEMP_SETUP; + } + } else if (this->state_ == STATE_TEMP_SETUP) { + // This should run once on the second update() after initialization. + if (this->temperature_sensor_ != nullptr) { + // This assumes that a thermistor is attached to the device as shown in the datahseet. + if (this->set_register_(LC709203F_STATUS_BIT, 0x0001) == i2c::NO_ERROR) { + if (this->set_register_(LC709203F_THERMISTOR_B, this->b_constant_) == i2c::NO_ERROR) { + this->state_ = STATE_NORMAL; + } + } + } else if (this->set_register_(LC709203F_STATUS_BIT, 0x0000) == i2c::NO_ERROR) { + // The device expects to get updates to the temperature in this mode. + // I am not doing that now. The temperature register defaults to 25C. + // In theory, we could have another temperature sensor and have ESPHome + // send updated temperature to the device occasionally, but I have no idea + // how to make that happen. + this->state_ = STATE_NORMAL; + } + } +} + +void Lc709203f::dump_config() { + ESP_LOGCONFIG(TAG, "LC709203F:"); + LOG_I2C_DEVICE(this); + + LOG_UPDATE_INTERVAL(this); + ESP_LOGCONFIG(TAG, + " Pack Size: %d mAH\n" + " Pack APA: 0x%02X", + this->pack_size_, this->apa_); + + // This is only true if the pack_voltage_ is either 0x0000 or 0x0001. The config validator + // should have already verified this. + ESP_LOGCONFIG(TAG, " Pack Rated Voltage: 3.%sV", this->pack_voltage_ == 0x0000 ? "8" : "7"); + + LOG_SENSOR(" ", "Voltage", this->voltage_sensor_); + LOG_SENSOR(" ", "Battery Remaining", this->battery_remaining_sensor_); + + if (this->temperature_sensor_ != nullptr) { + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); + ESP_LOGCONFIG(TAG, " B_Constant: %d", this->b_constant_); + } else { + ESP_LOGCONFIG(TAG, " No Temperature Sensor"); + } +} + +uint8_t Lc709203f::get_register_(uint8_t register_to_read, uint16_t *register_value) { + i2c::ErrorCode return_code; + uint8_t read_buffer[6]; + + read_buffer[0] = (this->address_) << 1; + read_buffer[1] = register_to_read; + read_buffer[2] = ((this->address_) << 1) | 0x01; + + for (uint8_t i = 0; i <= LC709203F_I2C_RETRY_COUNT; i++) { + // Note: the read_register() function does not send a stop between the write and + // the read portions of the I2C transation when you set the last variable to 'false' + // as we do below. Some of the other I2C read functions such as the generic read() + // function will send a stop between the read and the write portion of the I2C + // transaction. This is bad in this case and will result in reading nothing but 0xFFFF + // from the registers. + return_code = this->read_register(register_to_read, &read_buffer[3], 3, false); + if (return_code != i2c::NO_ERROR) { + // Error on the i2c bus + this->status_set_warning( + str_sprintf("Error code %d when reading from register 0x%02X", return_code, register_to_read).c_str()); + } else if (this->crc8_(read_buffer, 5) != read_buffer[5]) { + // I2C indicated OK, but the CRC of the data does not matcth. + this->status_set_warning(str_sprintf("CRC error reading from register 0x%02X", register_to_read).c_str()); + } else { + *register_value = ((uint16_t) read_buffer[4] << 8) | (uint16_t) read_buffer[3]; + return i2c::NO_ERROR; + } + } + + // If we get here, we tried LC709203F_I2C_RETRY_COUNT times to read the register and + // failed each time. Set the register value to 0 and return the I2C error code or 0xFF + // to indicate a CRC failure. It will be up to the higher level code what to do when + // this happens. + *register_value = 0x0000; + if (return_code != i2c::NO_ERROR) { + return return_code; + } else { + return 0xFF; + } +} + +uint8_t Lc709203f::set_register_(uint8_t register_to_set, uint16_t value_to_set) { + i2c::ErrorCode return_code; + uint8_t write_buffer[5]; + + // Note: We don't actually send byte[0] of the buffer. We include it because it is + // part of the CRC calculation. + write_buffer[0] = (this->address_) << 1; + write_buffer[1] = register_to_set; + write_buffer[2] = value_to_set & 0xFF; // Low byte + write_buffer[3] = (value_to_set >> 8) & 0xFF; // High byte + write_buffer[4] = this->crc8_(write_buffer, 4); + + for (uint8_t i = 0; i <= LC709203F_I2C_RETRY_COUNT; i++) { + // Note: we don't write the first byte of the write buffer to the device. + // This is done automatically by the write() function. + return_code = this->write(&write_buffer[1], 4, true); + if (return_code == i2c::NO_ERROR) { + return return_code; + } else { + this->status_set_warning( + str_sprintf("Error code %d when writing to register 0x%02X", return_code, register_to_set).c_str()); + } + } + + // If we get here, we tried to send the data LC709203F_I2C_RETRY_COUNT times and failed. + // We return the I2C error code, it is up to the higher level code what to do about it. + return return_code; +} + +uint8_t Lc709203f::crc8_(uint8_t *byte_buffer, uint8_t length_of_crc) { + uint8_t crc = 0x00; + const uint8_t polynomial(0x07); + + for (uint8_t j = length_of_crc; j; --j) { + crc ^= *byte_buffer++; + + for (uint8_t i = 8; i; --i) { + crc = (crc & 0x80) ? (crc << 1) ^ polynomial : (crc << 1); + } + } + return crc; +} + +void Lc709203f::set_pack_size(uint16_t pack_size) { + static const uint16_t PACK_SIZE_ARRAY[6] = {100, 200, 500, 1000, 2000, 3000}; + static const uint16_t APA_ARRAY[6] = {0x08, 0x0B, 0x10, 0x19, 0x2D, 0x36}; + float slope; + float intercept; + + this->pack_size_ = pack_size; // Pack size in mAH + + // The size is used to calculate the 'Adjustment Pack Application' number. + // Here we assume a type 01 or type 03 battery and do a linear curve fit to find the APA. + for (uint8_t i = 0; i < 6; i++) { + if (PACK_SIZE_ARRAY[i] == pack_size) { + // If the pack size is exactly one of the values in the array. + this->apa_ = APA_ARRAY[i]; + return; + } else if ((i > 0) && (PACK_SIZE_ARRAY[i] > pack_size) && (PACK_SIZE_ARRAY[i - 1] < pack_size)) { + // If the pack size is between the current array element and the previous. Do a linear + // Curve fit to determine the APA value. + + // Type casting is required here to avoid interger division + slope = static_cast(APA_ARRAY[i] - APA_ARRAY[i - 1]) / + static_cast(PACK_SIZE_ARRAY[i] - PACK_SIZE_ARRAY[i - 1]); + + // Type casting might not be needed here. + intercept = static_cast(APA_ARRAY[i]) - slope * static_cast(PACK_SIZE_ARRAY[i]); + + this->apa_ = static_cast(slope * pack_size + intercept); + return; + } + } + // We should never get here. If we do, it means we never set the pack APA. This should + // not be possible because of the config validation. However, if it does happen, the + // consequence is that the RSOC values will likley not be as accurate. However, it should + // not cause an error or crash, so I am not doing any additional checking here. +} + +void Lc709203f::set_thermistor_b_constant(uint16_t b_constant) { this->b_constant_ = b_constant; } + +void Lc709203f::set_pack_voltage(LC709203FBatteryVoltage pack_voltage) { this->pack_voltage_ = pack_voltage; } + +} // namespace lc709203f +} // namespace esphome diff --git a/esphome/components/lc709203f/lc709203f.h b/esphome/components/lc709203f/lc709203f.h new file mode 100644 index 0000000000..3b5b04775f --- /dev/null +++ b/esphome/components/lc709203f/lc709203f.h @@ -0,0 +1,55 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace lc709203f { + +enum LC709203FState { + STATE_INIT, + STATE_RSOC, + STATE_TEMP_SETUP, + STATE_NORMAL, +}; + +/// Enum listing allowable voltage settings for the LC709203F. +enum LC709203FBatteryVoltage { + LC709203F_BATTERY_VOLTAGE_3_8 = 0x0000, + LC709203F_BATTERY_VOLTAGE_3_7 = 0x0001, +}; + +class Lc709203f : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + void update() override; + void dump_config() override; + + void set_pack_size(uint16_t pack_size); + void set_thermistor_b_constant(uint16_t b_constant); + void set_pack_voltage(LC709203FBatteryVoltage pack_voltage); + void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } + void set_battery_remaining_sensor(sensor::Sensor *battery_remaining_sensor) { + battery_remaining_sensor_ = battery_remaining_sensor; + } + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } + + private: + uint8_t get_register_(uint8_t register_to_read, uint16_t *register_value); + uint8_t set_register_(uint8_t register_to_set, uint16_t value_to_set); + uint8_t crc8_(uint8_t *byte_buffer, uint8_t length_of_crc); + + protected: + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *battery_remaining_sensor_{nullptr}; + sensor::Sensor *temperature_sensor_{nullptr}; + uint16_t pack_size_; + uint16_t apa_; + uint16_t b_constant_; + LC709203FState state_ = STATE_INIT; + uint16_t pack_voltage_; +}; + +} // namespace lc709203f +} // namespace esphome diff --git a/esphome/components/lc709203f/sensor.py b/esphome/components/lc709203f/sensor.py new file mode 100644 index 0000000000..eb08a522e5 --- /dev/null +++ b/esphome/components/lc709203f/sensor.py @@ -0,0 +1,93 @@ +import esphome.codegen as cg +from esphome.components import i2c, sensor +import esphome.config_validation as cv +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_BATTERY_VOLTAGE, + CONF_ID, + CONF_SIZE, + CONF_TEMPERATURE, + CONF_VOLTAGE, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_VOLTAGE, + ENTITY_CATEGORY_DIAGNOSTIC, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_PERCENT, + UNIT_VOLT, +) + +DEPENDENCIES = ["i2c"] + +lc709203f_ns = cg.esphome_ns.namespace("lc709203f") + +CONF_B_CONSTANT = "b_constant" + +LC709203FBatteryVoltage = lc709203f_ns.enum("LC709203FBatteryVoltage") +BATTERY_VOLTAGE_OPTIONS = { + "3.7": LC709203FBatteryVoltage.LC709203F_BATTERY_VOLTAGE_3_7, + "3.8": LC709203FBatteryVoltage.LC709203F_BATTERY_VOLTAGE_3_8, +} + +lc709203f = lc709203f_ns.class_("Lc709203f", cg.PollingComponent, i2c.I2CDevice) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(lc709203f), + cv.Optional(CONF_SIZE, default="500"): cv.int_range(100, 3000), + cv.Optional(CONF_VOLTAGE, default="3.7"): cv.enum( + BATTERY_VOLTAGE_OPTIONS, upper=True + ), + cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema( + unit_of_measurement=UNIT_VOLT, + accuracy_decimals=3, + device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + accuracy_decimals=3, + device_class=DEVICE_CLASS_BATTERY, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ).extend( + { + cv.Required(CONF_B_CONSTANT): cv.int_range(0, 0xFFFF), + } + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x0B)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + cg.add(var.set_pack_size(config.get(CONF_SIZE))) + cg.add(var.set_pack_voltage(BATTERY_VOLTAGE_OPTIONS[config[CONF_VOLTAGE]])) + + if voltage_config := config.get(CONF_BATTERY_VOLTAGE): + sens = await sensor.new_sensor(voltage_config) + cg.add(var.set_voltage_sensor(sens)) + + if level_config := config.get(CONF_BATTERY_LEVEL): + sens = await sensor.new_sensor(level_config) + cg.add(var.set_battery_remaining_sensor(sens)) + + if temp_config := config.get(CONF_TEMPERATURE): + sens = await sensor.new_sensor(temp_config) + cg.add(var.set_temperature_sensor(sens)) + cg.add(var.set_thermistor_b_constant(temp_config[CONF_B_CONSTANT])) diff --git a/esphome/components/lcd_base/lcd_display.cpp b/esphome/components/lcd_base/lcd_display.cpp index 65d7aa508f..d3434cce10 100644 --- a/esphome/components/lcd_base/lcd_display.cpp +++ b/esphome/components/lcd_base/lcd_display.cpp @@ -1,7 +1,7 @@ #include "lcd_display.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace lcd_base { diff --git a/esphome/components/lcd_gpio/gpio_lcd_display.cpp b/esphome/components/lcd_gpio/gpio_lcd_display.cpp index a738893816..afa74643fb 100644 --- a/esphome/components/lcd_gpio/gpio_lcd_display.cpp +++ b/esphome/components/lcd_gpio/gpio_lcd_display.cpp @@ -7,7 +7,7 @@ namespace lcd_gpio { static const char *const TAG = "lcd_gpio"; void GPIOLCDDisplay::setup() { - ESP_LOGCONFIG(TAG, "Setting up GPIO LCD Display..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->rs_pin_->setup(); // OUTPUT this->rs_pin_->digital_write(false); if (this->rw_pin_ != nullptr) { @@ -24,8 +24,10 @@ void GPIOLCDDisplay::setup() { LCDDisplay::setup(); } void GPIOLCDDisplay::dump_config() { - ESP_LOGCONFIG(TAG, "GPIO LCD Display:"); - ESP_LOGCONFIG(TAG, " Columns: %u, Rows: %u", this->columns_, this->rows_); + ESP_LOGCONFIG(TAG, + "GPIO LCD Display:\n" + " Columns: %u, Rows: %u", + this->columns_, this->rows_); LOG_PIN(" RS Pin: ", this->rs_pin_); LOG_PIN(" RW Pin: ", this->rw_pin_); LOG_PIN(" Enable Pin: ", this->enable_pin_); diff --git a/esphome/components/lcd_menu/lcd_menu.cpp b/esphome/components/lcd_menu/lcd_menu.cpp index 74ada5e584..c664b394bf 100644 --- a/esphome/components/lcd_menu/lcd_menu.cpp +++ b/esphome/components/lcd_menu/lcd_menu.cpp @@ -19,10 +19,12 @@ void LCDCharacterMenuComponent::setup() { float LCDCharacterMenuComponent::get_setup_priority() const { return setup_priority::PROCESSOR - 1.0f; } void LCDCharacterMenuComponent::dump_config() { - ESP_LOGCONFIG(TAG, "LCD Menu"); - ESP_LOGCONFIG(TAG, " Columns: %u, Rows: %u", this->columns_, this->rows_); - ESP_LOGCONFIG(TAG, " Mark characters: %02x, %02x, %02x, %02x", this->mark_selected_, this->mark_editing_, - this->mark_submenu_, this->mark_back_); + ESP_LOGCONFIG(TAG, + "LCD Menu\n" + " Columns: %u, Rows: %u\n" + " Mark characters: %02x, %02x, %02x, %02x", + this->columns_, this->rows_, this->mark_selected_, this->mark_editing_, this->mark_submenu_, + this->mark_back_); if (this->is_failed()) { ESP_LOGE(TAG, "The connected display failed, the menu is disabled!"); } diff --git a/esphome/components/lcd_pcf8574/pcf8574_display.cpp b/esphome/components/lcd_pcf8574/pcf8574_display.cpp index 5b00b08aff..0f06548b13 100644 --- a/esphome/components/lcd_pcf8574/pcf8574_display.cpp +++ b/esphome/components/lcd_pcf8574/pcf8574_display.cpp @@ -11,7 +11,7 @@ static const uint8_t LCD_DISPLAY_BACKLIGHT_ON = 0x08; static const uint8_t LCD_DISPLAY_BACKLIGHT_OFF = 0x00; void PCF8574LCDDisplay::setup() { - ESP_LOGCONFIG(TAG, "Setting up PCF8574 LCD Display..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->backlight_value_ = LCD_DISPLAY_BACKLIGHT_ON; if (!this->write_bytes(this->backlight_value_, nullptr, 0)) { this->mark_failed(); @@ -21,12 +21,14 @@ void PCF8574LCDDisplay::setup() { LCDDisplay::setup(); } void PCF8574LCDDisplay::dump_config() { - ESP_LOGCONFIG(TAG, "PCF8574 LCD Display:"); - ESP_LOGCONFIG(TAG, " Columns: %u, Rows: %u", this->columns_, this->rows_); + ESP_LOGCONFIG(TAG, + "PCF8574 LCD Display:\n" + " Columns: %u, Rows: %u", + this->columns_, this->rows_); LOG_I2C_DEVICE(this); LOG_UPDATE_INTERVAL(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with LCD Display failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } void PCF8574LCDDisplay::write_n_bits(uint8_t value, uint8_t n) { diff --git a/esphome/components/ld2410/ld2410.cpp b/esphome/components/ld2410/ld2410.cpp index c3b57815d6..d7007ae0bd 100644 --- a/esphome/components/ld2410/ld2410.cpp +++ b/esphome/components/ld2410/ld2410.cpp @@ -72,17 +72,16 @@ void LD2410Component::dump_config() { } #endif this->read_all_info(); - ESP_LOGCONFIG(TAG, " Throttle_ : %ums", this->throttle_); - ESP_LOGCONFIG(TAG, " MAC Address : %s", const_cast(this->mac_.c_str())); - ESP_LOGCONFIG(TAG, " Firmware Version : %s", const_cast(this->version_.c_str())); + ESP_LOGCONFIG(TAG, + " Throttle_ : %ums\n" + " MAC Address : %s\n" + " Firmware Version : %s", + this->throttle_, const_cast(this->mac_.c_str()), const_cast(this->version_.c_str())); } void LD2410Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up LD2410..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->read_all_info(); - ESP_LOGCONFIG(TAG, "Mac Address : %s", const_cast(this->mac_.c_str())); - ESP_LOGCONFIG(TAG, "Firmware Version : %s", const_cast(this->version_.c_str())); - ESP_LOGCONFIG(TAG, "LD2410 setup complete."); } void LD2410Component::read_all_info() { diff --git a/esphome/components/ld2420/ld2420.cpp b/esphome/components/ld2420/ld2420.cpp index 9d628cc14f..5b3206bf12 100644 --- a/esphome/components/ld2420/ld2420.cpp +++ b/esphome/components/ld2420/ld2420.cpp @@ -65,9 +65,11 @@ static const char *const TAG = "ld2420"; float LD2420Component::get_setup_priority() const { return setup_priority::BUS; } void LD2420Component::dump_config() { - ESP_LOGCONFIG(TAG, "LD2420:"); - ESP_LOGCONFIG(TAG, " Firmware Version : %7s", this->ld2420_firmware_ver_); - ESP_LOGCONFIG(TAG, "LD2420 Number:"); + ESP_LOGCONFIG(TAG, + "LD2420:\n" + " Firmware Version : %7s\n" + "LD2420 Number:", + this->ld2420_firmware_ver_); #ifdef USE_NUMBER LOG_NUMBER(TAG, " Gate Timeout:", this->gate_timeout_number_); LOG_NUMBER(TAG, " Gate Max Distance:", this->max_gate_distance_number_); @@ -111,7 +113,7 @@ int LD2420Component::get_firmware_int_(const char *version_string) { } void LD2420Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up LD2420..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections."); this->mark_failed(); @@ -158,7 +160,7 @@ void LD2420Component::apply_config_action() { ESP_LOGCONFIG(TAG, "No configuration change detected"); return; } - ESP_LOGCONFIG(TAG, "Reconfiguring LD2420..."); + ESP_LOGCONFIG(TAG, "Reconfiguring LD2420"); if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections."); this->mark_failed(); @@ -180,7 +182,7 @@ void LD2420Component::apply_config_action() { } void LD2420Component::factory_reset_action() { - ESP_LOGCONFIG(TAG, "Setting factory defaults..."); + ESP_LOGCONFIG(TAG, "Setting factory defaults"); if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections."); this->mark_failed(); @@ -209,7 +211,7 @@ void LD2420Component::factory_reset_action() { } void LD2420Component::restart_module_action() { - ESP_LOGCONFIG(TAG, "Restarting LD2420 module..."); + ESP_LOGCONFIG(TAG, "Restarting LD2420 module"); this->send_module_restart(); this->set_timeout(250, [this]() { this->set_config_mode(true); diff --git a/esphome/components/ld2450/ld2450.cpp b/esphome/components/ld2450/ld2450.cpp index 3db065ba1a..519e4d89a3 100644 --- a/esphome/components/ld2450/ld2450.cpp +++ b/esphome/components/ld2450/ld2450.cpp @@ -109,7 +109,7 @@ static inline std::string format_version(uint8_t *buffer) { LD2450Component::LD2450Component() {} void LD2450Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up HLK-LD2450..."); + ESP_LOGCONFIG(TAG, "Running setup"); #ifdef USE_NUMBER if (this->presence_timeout_number_ != nullptr) { this->pref_ = global_preferences->make_preference(this->presence_timeout_number_->get_object_id_hash()); @@ -188,9 +188,11 @@ void LD2450Component::dump_config() { #ifdef USE_NUMBER LOG_NUMBER(" ", "PresenceTimeoutNumber", this->presence_timeout_number_); #endif - ESP_LOGCONFIG(TAG, " Throttle : %ums", this->throttle_); - ESP_LOGCONFIG(TAG, " MAC Address : %s", const_cast(this->mac_.c_str())); - ESP_LOGCONFIG(TAG, " Firmware version : %s", const_cast(this->version_.c_str())); + ESP_LOGCONFIG(TAG, + " Throttle : %ums\n" + " MAC Address : %s\n" + " Firmware version : %s", + this->throttle_, const_cast(this->mac_.c_str()), const_cast(this->version_.c_str())); } void LD2450Component::loop() { diff --git a/esphome/components/ld2450/sensor.py b/esphome/components/ld2450/sensor.py index 21580c5801..071ce8aa32 100644 --- a/esphome/components/ld2450/sensor.py +++ b/esphome/components/ld2450/sensor.py @@ -6,6 +6,8 @@ from esphome.const import ( CONF_DISTANCE, CONF_RESOLUTION, CONF_SPEED, + CONF_X, + CONF_Y, DEVICE_CLASS_DISTANCE, DEVICE_CLASS_SPEED, UNIT_DEGREES, @@ -19,8 +21,6 @@ DEPENDENCIES = ["ld2450"] CONF_MOVING_TARGET_COUNT = "moving_target_count" CONF_STILL_TARGET_COUNT = "still_target_count" CONF_TARGET_COUNT = "target_count" -CONF_X = "x" -CONF_Y = "y" ICON_ACCOUNT_GROUP = "mdi:account-group" ICON_ACCOUNT_SWITCH = "mdi:account-switch" diff --git a/esphome/components/ledc/ledc_output.cpp b/esphome/components/ledc/ledc_output.cpp index 4ced4b8f76..aefe0e63d8 100644 --- a/esphome/components/ledc/ledc_output.cpp +++ b/esphome/components/ledc/ledc_output.cpp @@ -46,11 +46,13 @@ 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 static_cast(CLOCK_FREQUENCY) / static_cast(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 static_cast(CLOCK_FREQUENCY) / (max_div_num * static_cast(1 << bit_depth)); } optional ledc_bit_depth_for_frequency(float frequency) { @@ -100,13 +102,13 @@ esp_err_t configure_timer_frequency(ledc_mode_t speed_mode, ledc_timer_t timer_n #ifdef USE_ESP_IDF constexpr int ledc_angle_to_htop(float angle, uint8_t bit_depth) { - return static_cast(angle * ((1U << bit_depth) - 1) / 360.); + return static_cast(angle * ((1U << bit_depth) - 1) / 360.0f); } #endif // USE_ESP_IDF void LEDCOutput::write_state(float state) { - if (!initialized_) { - ESP_LOGW(TAG, "LEDC output hasn't been initialized yet!"); + if (!this->initialized_) { + ESP_LOGW(TAG, "Not yet initialized"); return; } @@ -122,8 +124,8 @@ void LEDCOutput::write_state(float state) { ledcWrite(this->channel_, duty); #endif #ifdef USE_ESP_IDF - auto speed_mode = get_speed_mode(channel_); - auto chan_num = static_cast(channel_ % 8); + auto speed_mode = get_speed_mode(this->channel_); + auto chan_num = static_cast(this->channel_ % 8); int hpoint = ledc_angle_to_htop(this->phase_angle_, this->bit_depth_); if (duty == max_duty) { ledc_stop(speed_mode, chan_num, 1); @@ -137,7 +139,7 @@ void LEDCOutput::write_state(float state) { } void LEDCOutput::setup() { - ESP_LOGV(TAG, "Entering setup..."); + ESP_LOGCONFIG(TAG, "Running setup"); #ifdef USE_ARDUINO this->update_frequency(this->frequency_); this->turn_off(); @@ -145,9 +147,9 @@ void LEDCOutput::setup() { ledcAttachPin(this->pin_->get_pin(), this->channel_); #endif #ifdef USE_ESP_IDF - auto speed_mode = get_speed_mode(channel_); - auto timer_num = static_cast((channel_ % 8) / 2); - auto chan_num = static_cast(channel_ % 8); + auto speed_mode = get_speed_mode(this->channel_); + auto timer_num = static_cast((this->channel_ % 8) / 2); + auto chan_num = static_cast(this->channel_ % 8); esp_err_t timer_init_result = configure_timer_frequency(speed_mode, timer_num, chan_num, this->channel_, this->bit_depth_, this->frequency_); @@ -163,26 +165,28 @@ void LEDCOutput::setup() { ESP_LOGV(TAG, "Angle of %.1f° results in hpoint %u", this->phase_angle_, hpoint); ledc_channel_config_t chan_conf{}; - chan_conf.gpio_num = pin_->get_pin(); + chan_conf.gpio_num = this->pin_->get_pin(); chan_conf.speed_mode = speed_mode; chan_conf.channel = chan_num; chan_conf.intr_type = LEDC_INTR_DISABLE; chan_conf.timer_sel = timer_num; - chan_conf.duty = inverted_ == pin_->is_inverted() ? 0 : (1U << bit_depth_); + chan_conf.duty = this->inverted_ == this->pin_->is_inverted() ? 0 : (1U << this->bit_depth_); chan_conf.hpoint = hpoint; ledc_channel_config(&chan_conf); - initialized_ = true; + this->initialized_ = true; this->status_clear_error(); #endif } void LEDCOutput::dump_config() { - ESP_LOGCONFIG(TAG, "LEDC Output:"); + ESP_LOGCONFIG(TAG, "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, " Phase angle: %.1f°", this->phase_angle_); - ESP_LOGCONFIG(TAG, " Bit depth: %u", this->bit_depth_); + ESP_LOGCONFIG(TAG, + " Channel: %u\n" + " PWM Frequency: %.1f Hz\n" + " Phase angle: %.1f°\n" + " Bit depth: %u", + this->channel_, this->frequency_, this->phase_angle_, 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))); @@ -205,17 +209,17 @@ void LEDCOutput::update_frequency(float frequency) { 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..."); + 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_); + ESP_LOGV(TAG, "Initializing 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->initialized_ = true; this->status_clear_error(); ESP_LOGV(TAG, "Configured frequency: %u with bit depth: %u", configured_frequency, this->bit_depth_); } else { @@ -236,14 +240,14 @@ void LEDCOutput::update_frequency(float frequency) { #endif // USE_ARDUINO #ifdef USE_ESP_IDF - if (!initialized_) { - ESP_LOGW(TAG, "LEDC output hasn't been initialized yet!"); + if (!this->initialized_) { + ESP_LOGW(TAG, "Not yet initialized"); return; } - auto speed_mode = get_speed_mode(channel_); - auto timer_num = static_cast((channel_ % 8) / 2); - auto chan_num = static_cast(channel_ % 8); + auto speed_mode = get_speed_mode(this->channel_); + auto timer_num = static_cast((this->channel_ % 8) / 2); + auto chan_num = static_cast(this->channel_ % 8); esp_err_t timer_init_result = configure_timer_frequency(speed_mode, timer_num, chan_num, this->channel_, this->bit_depth_, this->frequency_); diff --git a/esphome/components/libretiny/const.py b/esphome/components/libretiny/const.py index 525d8b7786..362609df44 100644 --- a/esphome/components/libretiny/const.py +++ b/esphome/components/libretiny/const.py @@ -1,5 +1,5 @@ +from collections.abc import Callable from dataclasses import dataclass -from typing import Callable import esphome.codegen as cg diff --git a/esphome/components/libretiny/preferences.cpp b/esphome/components/libretiny/preferences.cpp index a090f42aa7..ce4ed915c0 100644 --- a/esphome/components/libretiny/preferences.cpp +++ b/esphome/components/libretiny/preferences.cpp @@ -1,8 +1,8 @@ #ifdef USE_LIBRETINY -#include "esphome/core/preferences.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "esphome/core/preferences.h" #include #include #include @@ -101,7 +101,7 @@ class LibreTinyPreferences : public ESPPreferences { if (s_pending_save.empty()) return true; - ESP_LOGD(TAG, "Saving %d preferences to flash...", s_pending_save.size()); + ESP_LOGV(TAG, "Saving %d items...", s_pending_save.size()); // goal try write all pending saves even if one fails int cached = 0, written = 0, failed = 0; fdb_err_t last_err = FDB_NO_ERR; @@ -129,11 +129,10 @@ class LibreTinyPreferences : public ESPPreferences { } s_pending_save.erase(s_pending_save.begin() + i); } - ESP_LOGD(TAG, "Saving %d preferences to flash: %d cached, %d written, %d failed", cached + written + failed, cached, - written, failed); + ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written, + failed); if (failed > 0) { - ESP_LOGE(TAG, "Error saving %d preferences to flash. Last error=%d for key=%s", failed, last_err, - last_key.c_str()); + ESP_LOGE(TAG, "Writing %d items failed. Last error=%d for key=%s", failed, last_err, last_key.c_str()); } return failed == 0; @@ -158,7 +157,7 @@ class LibreTinyPreferences : public ESPPreferences { } bool reset() override { - ESP_LOGD(TAG, "Cleaning up preferences in flash..."); + ESP_LOGD(TAG, "Erasing storage"); s_pending_save.clear(); fdb_kv_set_default(&db); diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index 237ab45f38..a013029fc2 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -37,7 +37,7 @@ from esphome.const import ( CONF_WEB_SERVER, CONF_WHITE, ) -from esphome.core import coroutine_with_priority +from esphome.core import CORE, coroutine_with_priority from esphome.cpp_generator import MockObjClass from esphome.cpp_helpers import setup_entity @@ -270,6 +270,7 @@ async def setup_light_core_(light_var, output_var, config): async def register_light(output_var, config): light_var = cg.new_Pvariable(config[CONF_ID], output_var) cg.add(cg.App.register_light(light_var)) + CORE.register_platform_component("light", light_var) await cg.register_component(light_var, config) await setup_light_core_(light_var, output_var, config) diff --git a/esphome/components/light/esp_hsv_color.h b/esphome/components/light/esp_hsv_color.h index 39f5e55707..cdde91c71c 100644 --- a/esphome/components/light/esp_hsv_color.h +++ b/esphome/components/light/esp_hsv_color.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/helpers.h" #include "esphome/core/color.h" +#include "esphome/core/helpers.h" namespace esphome { namespace light { diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index 16b78a17bd..0aae6aed15 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -18,7 +18,7 @@ LightCall LightState::toggle() { return this->make_call().set_state(!this->remot LightCall LightState::make_call() { return LightCall(this); } void LightState::setup() { - ESP_LOGCONFIG(TAG, "Setting up light '%s'...", this->get_name().c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str()); this->output_->setup_state(this); for (auto *effect : this->effects_) { @@ -91,12 +91,16 @@ void LightState::setup() { void LightState::dump_config() { ESP_LOGCONFIG(TAG, "Light '%s'", this->get_name().c_str()); if (this->get_traits().supports_color_capability(ColorCapability::BRIGHTNESS)) { - ESP_LOGCONFIG(TAG, " Default Transition Length: %.1fs", this->default_transition_length_ / 1e3f); - ESP_LOGCONFIG(TAG, " Gamma Correct: %.2f", this->gamma_correct_); + ESP_LOGCONFIG(TAG, + " Default Transition Length: %.1fs\n" + " Gamma Correct: %.2f", + this->default_transition_length_ / 1e3f, this->gamma_correct_); } if (this->get_traits().supports_color_capability(ColorCapability::COLOR_TEMPERATURE)) { - ESP_LOGCONFIG(TAG, " Min Mireds: %.1f", this->get_traits().get_min_mireds()); - ESP_LOGCONFIG(TAG, " Max Mireds: %.1f", this->get_traits().get_max_mireds()); + ESP_LOGCONFIG(TAG, + " Min Mireds: %.1f\n" + " Max Mireds: %.1f", + this->get_traits().get_min_mireds(), this->get_traits().get_max_mireds()); } } void LightState::loop() { diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h index acba986f24..b93823feac 100644 --- a/esphome/components/light/light_state.h +++ b/esphome/components/light/light_state.h @@ -17,7 +17,7 @@ namespace light { class LightOutput; -enum LightRestoreMode { +enum LightRestoreMode : uint8_t { LIGHT_RESTORE_DEFAULT_OFF, LIGHT_RESTORE_DEFAULT_ON, LIGHT_ALWAYS_OFF, @@ -212,12 +212,18 @@ class LightState : public EntityBase, public Component { /// Store the output to allow effects to have more access. LightOutput *output_; - /// Value for storing the index of the currently active effect. 0 if no effect is active - uint32_t active_effect_index_{}; /// The currently active transformer for this light (transition/flash). std::unique_ptr transformer_{nullptr}; - /// Whether the light value should be written in the next cycle. - bool next_write_{true}; + /// List of effects for this light. + std::vector effects_; + /// Value for storing the index of the currently active effect. 0 if no effect is active + uint32_t active_effect_index_{}; + /// Default transition length for all transitions in ms. + uint32_t default_transition_length_{}; + /// Transition length to use for flash transitions. + uint32_t flash_transition_length_{}; + /// Gamma correction factor for the light. + float gamma_correct_{}; /// Object used to store the persisted values of the light. ESPPreferenceObject rtc_; @@ -236,19 +242,13 @@ class LightState : public EntityBase, public Component { */ CallbackManager target_state_reached_callback_{}; - /// Default transition length for all transitions in ms. - uint32_t default_transition_length_{}; - /// Transition length to use for flash transitions. - uint32_t flash_transition_length_{}; - /// Gamma correction factor for the light. - float gamma_correct_{}; - /// Restore mode of the light. - LightRestoreMode restore_mode_; /// Initial state of the light. optional initial_state_{}; - /// List of effects for this light. - std::vector effects_; + /// Restore mode of the light. + LightRestoreMode restore_mode_; + /// Whether the light value should be written in the next cycle. + bool next_write_{true}; // for effects, true if a transformer (transition) is active. bool is_transformer_active_ = false; }; diff --git a/esphome/components/light/light_transformer.h b/esphome/components/light/light_transformer.h index 35b045d5b4..fb9b709187 100644 --- a/esphome/components/light/light_transformer.h +++ b/esphome/components/light/light_transformer.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #include "light_color_values.h" namespace esphome { diff --git a/esphome/components/lightwaverf/LwTx.cpp b/esphome/components/lightwaverf/LwTx.cpp index 2f46b04b2d..f5ef6ddb2c 100644 --- a/esphome/components/lightwaverf/LwTx.cpp +++ b/esphome/components/lightwaverf/LwTx.cpp @@ -8,8 +8,8 @@ #include "LwTx.h" #include #include -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace lightwaverf { diff --git a/esphome/components/lightwaverf/lightwaverf.cpp b/esphome/components/lightwaverf/lightwaverf.cpp index 89cbdae6e1..626e5747b7 100644 --- a/esphome/components/lightwaverf/lightwaverf.cpp +++ b/esphome/components/lightwaverf/lightwaverf.cpp @@ -14,7 +14,7 @@ static const bool DEFAULT_INVERT = false; static const uint32_t DEFAULT_TICK = 330; void LightWaveRF::setup() { - ESP_LOGCONFIG(TAG, "Setting up Lightwave RF..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->lwtx_.lwtx_setup(pin_tx_, DEFAULT_REPEAT, DEFAULT_INVERT, DEFAULT_TICK); this->lwrx_.lwrx_setup(pin_rx_); diff --git a/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp b/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp index 58f2a42812..c472a9f669 100644 --- a/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp +++ b/esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp @@ -24,7 +24,7 @@ static const uint8_t READ_TOUCH[1] = {0x07}; } void LilygoT547Touchscreen::setup() { - ESP_LOGCONFIG(TAG, "Setting up Lilygo T5 4.7 Touchscreen..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); this->interrupt_pin_->setup(); diff --git a/esphome/components/lock/__init__.py b/esphome/components/lock/__init__.py index a96290dca6..0fb67e3948 100644 --- a/esphome/components/lock/__init__.py +++ b/esphome/components/lock/__init__.py @@ -115,6 +115,7 @@ async def register_lock(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_lock(var)) + CORE.register_platform_component("lock", var) await _setup_lock_core(var, config) diff --git a/esphome/components/lock/lock.h b/esphome/components/lock/lock.h index 7a98187a4f..2173c84903 100644 --- a/esphome/components/lock/lock.h +++ b/esphome/components/lock/lock.h @@ -2,9 +2,9 @@ #include "esphome/core/component.h" #include "esphome/core/entity_base.h" -#include "esphome/core/preferences.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "esphome/core/preferences.h" #include namespace esphome { diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 4136480629..462cae73b6 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -8,8 +8,10 @@ from esphome.components.esp32.const import ( VARIANT_ESP32, VARIANT_ESP32C2, VARIANT_ESP32C3, + VARIANT_ESP32C5, VARIANT_ESP32C6, VARIANT_ESP32H2, + VARIANT_ESP32P4, VARIANT_ESP32S2, VARIANT_ESP32S3, ) @@ -88,8 +90,10 @@ UART_SELECTION_ESP32 = { VARIANT_ESP32S3: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], VARIANT_ESP32C3: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], VARIANT_ESP32C2: [UART0, UART1], + VARIANT_ESP32C5: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], VARIANT_ESP32C6: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], VARIANT_ESP32H2: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], + VARIANT_ESP32P4: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], } UART_SELECTION_ESP8266 = [UART0, UART0_SWAP, UART1] @@ -205,8 +209,10 @@ CONFIG_SCHEMA = cv.All( esp32_s3_idf=USB_SERIAL_JTAG, esp32_c3_arduino=USB_CDC, esp32_c3_idf=USB_SERIAL_JTAG, + esp32_c5_idf=USB_SERIAL_JTAG, esp32_c6_arduino=USB_CDC, esp32_c6_idf=USB_SERIAL_JTAG, + esp32_p4_idf=USB_SERIAL_JTAG, rp2040=USB_CDC, bk72xx=DEFAULT, rtl87xx=DEFAULT, diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 014f7e3dec..28a66b23b7 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -116,7 +116,7 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr if (this->baud_rate_ > 0) { this->write_msg_(this->tx_buffer_ + msg_start); } - this->call_log_callbacks_(level, tag, this->tx_buffer_ + msg_start); + this->log_callback_.call(level, tag, this->tx_buffer_ + msg_start); global_recursion_guard_ = false; } @@ -129,19 +129,6 @@ inline int Logger::level_for(const char *tag) { return this->current_level_; } -void HOT Logger::call_log_callbacks_(int level, const char *tag, const char *msg) { -#ifdef USE_ESP32 - // Suppress network-logging if memory constrained - // In some configurations (eg BLE enabled) there may be some transient - // memory exhaustion, and trying to log when OOM can lead to a crash. Skipping - // here usually allows the stack to recover instead. - // See issue #1234 for analysis. - if (xPortGetFreeHeapSize() < 2048) - return; -#endif - this->log_callback_.call(level, tag, msg); -} - Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) { // add 1 to buffer size for null terminator this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT @@ -189,7 +176,7 @@ void Logger::loop() { this->tx_buffer_size_); this->write_footer_to_buffer_(this->tx_buffer_, &this->tx_buffer_at_, this->tx_buffer_size_); this->tx_buffer_[this->tx_buffer_at_] = '\0'; - this->call_log_callbacks_(message->level, message->tag, this->tx_buffer_); + this->log_callback_.call(message->level, message->tag, this->tx_buffer_); // At this point all the data we need from message has been transferred to the tx_buffer // so we can release the message to allow other tasks to use it as soon as possible. this->log_buffer_->release_message_main_loop(received_token); @@ -221,12 +208,16 @@ float Logger::get_setup_priority() const { return setup_priority::BUS + 500.0f; static const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DEBUG", "VERBOSE", "VERY_VERBOSE"}; void Logger::dump_config() { - ESP_LOGCONFIG(TAG, "Logger:"); - ESP_LOGCONFIG(TAG, " Max Level: %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]); - ESP_LOGCONFIG(TAG, " Initial Level: %s", LOG_LEVELS[this->current_level_]); + ESP_LOGCONFIG(TAG, + "Logger:\n" + " Max Level: %s\n" + " Initial Level: %s", + LOG_LEVELS[ESPHOME_LOG_LEVEL], LOG_LEVELS[this->current_level_]); #ifndef USE_HOST - ESP_LOGCONFIG(TAG, " Log Baud Rate: %" PRIu32, this->baud_rate_); - ESP_LOGCONFIG(TAG, " Hardware UART: %s", get_uart_selection_()); + ESP_LOGCONFIG(TAG, + " Log Baud Rate: %" PRIu32 "\n" + " Hardware UART: %s", + this->baud_rate_, get_uart_selection_()); #endif #ifdef USE_ESPHOME_TASK_LOG_BUFFER if (this->log_buffer_) { diff --git a/esphome/components/logger/logger.h b/esphome/components/logger/logger.h index 6030d9e8f2..9f09208b66 100644 --- a/esphome/components/logger/logger.h +++ b/esphome/components/logger/logger.h @@ -156,7 +156,6 @@ class Logger : public Component { #endif protected: - void call_log_callbacks_(int level, const char *tag, const char *msg); void write_msg_(const char *msg); // Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator @@ -191,7 +190,7 @@ class Logger : public Component { if (this->baud_rate_ > 0) { this->write_msg_(this->tx_buffer_); // If logging is enabled, write to console } - this->call_log_callbacks_(level, tag, this->tx_buffer_); + this->log_callback_.call(level, tag, this->tx_buffer_); } // Write the body of the log message to the buffer diff --git a/esphome/components/logger/logger_esp32.cpp b/esphome/components/logger/logger_esp32.cpp index c9de3d815a..b5ac84a665 100644 --- a/esphome/components/logger/logger_esp32.cpp +++ b/esphome/components/logger/logger_esp32.cpp @@ -18,12 +18,12 @@ #endif #endif -#include "freertos/FreeRTOS.h" #include "esp_idf_version.h" +#include "freertos/FreeRTOS.h" +#include #include #include -#include #endif // USE_ESP_IDF @@ -174,11 +174,11 @@ void Logger::pre_setup() { #ifdef USE_ESP_IDF void HOT Logger::write_msg_(const char *msg) { if ( -#if defined(USE_ESP32_VARIANT_ESP32S2) +#if defined(USE_LOGGER_USB_CDC) && !defined(USE_LOGGER_USB_SERIAL_JTAG) this->uart_ == UART_SELECTION_USB_CDC -#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32H2) +#elif defined(USE_LOGGER_USB_SERIAL_JTAG) && !defined(USE_LOGGER_USB_CDC) this->uart_ == UART_SELECTION_USB_SERIAL_JTAG -#elif defined(USE_ESP32_VARIANT_ESP32S3) +#elif defined(USE_LOGGER_USB_CDC) && defined(USE_LOGGER_USB_SERIAL_JTAG) this->uart_ == UART_SELECTION_USB_CDC || this->uart_ == UART_SELECTION_USB_SERIAL_JTAG #else /* DISABLES CODE */ (false) // NOLINT diff --git a/esphome/components/ltr390/ltr390.cpp b/esphome/components/ltr390/ltr390.cpp index 84b2eba2de..cc7e686d13 100644 --- a/esphome/components/ltr390/ltr390.cpp +++ b/esphome/components/ltr390/ltr390.cpp @@ -148,7 +148,7 @@ void LTR390Component::read_mode_(int mode_index) { } void LTR390Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up ltr390..."); + ESP_LOGCONFIG(TAG, "Running setup"); // reset std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get(); @@ -187,10 +187,13 @@ void LTR390Component::setup() { void LTR390Component::dump_config() { LOG_I2C_DEVICE(this); - ESP_LOGCONFIG(TAG, " ALS Gain: X%.0f", GAINVALUES[this->gain_als_]); - ESP_LOGCONFIG(TAG, " ALS Resolution: %u-bit", RESOLUTION_BITS[this->res_als_]); - ESP_LOGCONFIG(TAG, " UV Gain: X%.0f", GAINVALUES[this->gain_uv_]); - ESP_LOGCONFIG(TAG, " UV Resolution: %u-bit", RESOLUTION_BITS[this->res_uv_]); + ESP_LOGCONFIG(TAG, + " ALS Gain: X%.0f\n" + " ALS Resolution: %u-bit\n" + " UV Gain: X%.0f\n" + " UV Resolution: %u-bit", + GAINVALUES[this->gain_als_], RESOLUTION_BITS[this->res_als_], GAINVALUES[this->gain_uv_], + RESOLUTION_BITS[this->res_uv_]); } void LTR390Component::update() { diff --git a/esphome/components/ltr501/ltr501.cpp b/esphome/components/ltr501/ltr501.cpp index b30e520f2b..12f227ab91 100644 --- a/esphome/components/ltr501/ltr501.cpp +++ b/esphome/components/ltr501/ltr501.cpp @@ -1,7 +1,7 @@ #include "ltr501.h" #include "esphome/core/application.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" using esphome::i2c::ErrorCode; @@ -74,7 +74,7 @@ static float get_ps_gain_coeff(PsGain501 gain) { } void LTRAlsPs501Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up LTR-501/301/558"); + ESP_LOGCONFIG(TAG, "Running setup"); // As per datasheet we need to wait at least 100ms after power on to get ALS chip responsive this->set_timeout(100, [this]() { this->state_ = State::DELAYED_SETUP; }); } @@ -94,16 +94,21 @@ void LTRAlsPs501Component::dump_config() { }; LOG_I2C_DEVICE(this); - ESP_LOGCONFIG(TAG, " Device type: %s", get_device_type(this->ltr_type_)); - ESP_LOGCONFIG(TAG, " Automatic mode: %s", ONOFF(this->automatic_mode_enabled_)); - ESP_LOGCONFIG(TAG, " Gain: %.0fx", get_gain_coeff(this->gain_)); - ESP_LOGCONFIG(TAG, " Integration time: %d ms", get_itime_ms(this->integration_time_)); - ESP_LOGCONFIG(TAG, " Measurement repeat rate: %d ms", get_meas_time_ms(this->repeat_rate_)); - ESP_LOGCONFIG(TAG, " Glass attenuation factor: %f", this->glass_attenuation_factor_); - ESP_LOGCONFIG(TAG, " Proximity gain: %.0fx", get_ps_gain_coeff(this->ps_gain_)); - ESP_LOGCONFIG(TAG, " Proximity cooldown time: %d s", this->ps_cooldown_time_s_); - ESP_LOGCONFIG(TAG, " Proximity high threshold: %d", this->ps_threshold_high_); - ESP_LOGCONFIG(TAG, " Proximity low threshold: %d", this->ps_threshold_low_); + ESP_LOGCONFIG(TAG, + " Device type: %s\n" + " Automatic mode: %s\n" + " Gain: %.0fx\n" + " Integration time: %d ms\n" + " Measurement repeat rate: %d ms\n" + " Glass attenuation factor: %f\n" + " Proximity gain: %.0fx\n" + " Proximity cooldown time: %d s\n" + " Proximity high threshold: %d\n" + " Proximity low threshold: %d", + get_device_type(this->ltr_type_), ONOFF(this->automatic_mode_enabled_), get_gain_coeff(this->gain_), + get_itime_ms(this->integration_time_), get_meas_time_ms(this->repeat_rate_), + this->glass_attenuation_factor_, get_ps_gain_coeff(this->ps_gain_), this->ps_cooldown_time_s_, + this->ps_threshold_high_, this->ps_threshold_low_); LOG_UPDATE_INTERVAL(this); @@ -113,7 +118,7 @@ void LTRAlsPs501Component::dump_config() { LOG_SENSOR(" ", "Actual gain", this->actual_gain_sensor_); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with I2C LTR-501/301/558 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } @@ -306,7 +311,7 @@ void LTRAlsPs501Component::configure_als_() { uint8_t tries = MAX_TRIES; do { - ESP_LOGV(TAG, "Waiting for ALS device to become active..."); + ESP_LOGV(TAG, "Waiting for ALS device to become active"); delay(2); als_ctrl.raw = this->reg((uint8_t) CommandRegisters::ALS_CONTR).get(); } while (!als_ctrl.als_mode_active && tries--); // while active mode is not set - keep waiting diff --git a/esphome/components/ltr_als_ps/ltr_als_ps.cpp b/esphome/components/ltr_als_ps/ltr_als_ps.cpp index ae299c9b66..9b635a12b1 100644 --- a/esphome/components/ltr_als_ps/ltr_als_ps.cpp +++ b/esphome/components/ltr_als_ps/ltr_als_ps.cpp @@ -1,7 +1,7 @@ #include "ltr_als_ps.h" #include "esphome/core/application.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" using esphome::i2c::ErrorCode; @@ -63,7 +63,7 @@ static float get_ps_gain_coeff(PsGain gain) { } void LTRAlsPsComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up LTR-303/329/55x/659"); + ESP_LOGCONFIG(TAG, "Running setup"); // As per datasheet we need to wait at least 100ms after power on to get ALS chip responsive this->set_timeout(100, [this]() { this->state_ = State::DELAYED_SETUP; }); } @@ -85,27 +85,34 @@ void LTRAlsPsComponent::dump_config() { LOG_I2C_DEVICE(this); ESP_LOGCONFIG(TAG, " Device type: %s", get_device_type(this->ltr_type_)); if (this->is_als_()) { - ESP_LOGCONFIG(TAG, " Automatic mode: %s", ONOFF(this->automatic_mode_enabled_)); - ESP_LOGCONFIG(TAG, " Gain: %.0fx", get_gain_coeff(this->gain_)); - ESP_LOGCONFIG(TAG, " Integration time: %d ms", get_itime_ms(this->integration_time_)); - ESP_LOGCONFIG(TAG, " Measurement repeat rate: %d ms", get_meas_time_ms(this->repeat_rate_)); - ESP_LOGCONFIG(TAG, " Glass attenuation factor: %f", this->glass_attenuation_factor_); + ESP_LOGCONFIG(TAG, + " Automatic mode: %s\n" + " Gain: %.0fx\n" + " Integration time: %d ms\n" + " Measurement repeat rate: %d ms\n" + " Glass attenuation factor: %f", + ONOFF(this->automatic_mode_enabled_), get_gain_coeff(this->gain_), + get_itime_ms(this->integration_time_), get_meas_time_ms(this->repeat_rate_), + this->glass_attenuation_factor_); LOG_SENSOR(" ", "ALS calculated lux", this->ambient_light_sensor_); LOG_SENSOR(" ", "CH1 Infrared counts", this->infrared_counts_sensor_); LOG_SENSOR(" ", "CH0 Visible+IR counts", this->full_spectrum_counts_sensor_); LOG_SENSOR(" ", "Actual gain", this->actual_gain_sensor_); } if (this->is_ps_()) { - ESP_LOGCONFIG(TAG, " Proximity gain: %.0fx", get_ps_gain_coeff(this->ps_gain_)); - ESP_LOGCONFIG(TAG, " Proximity cooldown time: %d s", this->ps_cooldown_time_s_); - ESP_LOGCONFIG(TAG, " Proximity high threshold: %d", this->ps_threshold_high_); - ESP_LOGCONFIG(TAG, " Proximity low threshold: %d", this->ps_threshold_low_); + ESP_LOGCONFIG(TAG, + " Proximity gain: %.0fx\n" + " Proximity cooldown time: %d s\n" + " Proximity high threshold: %d\n" + " Proximity low threshold: %d", + get_ps_gain_coeff(this->ps_gain_), this->ps_cooldown_time_s_, this->ps_threshold_high_, + this->ps_threshold_low_); LOG_SENSOR(" ", "Proximity counts", this->proximity_counts_sensor_); } LOG_UPDATE_INTERVAL(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with I2C LTR-303/329/55x/659 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } @@ -298,7 +305,7 @@ void LTRAlsPsComponent::configure_als_() { uint8_t tries = MAX_TRIES; do { - ESP_LOGV(TAG, "Waiting for device to become active..."); + ESP_LOGV(TAG, "Waiting for device to become active"); delay(2); als_ctrl.raw = this->reg((uint8_t) CommandRegisters::ALS_CONTR).get(); } while (!als_ctrl.active_mode && tries--); // while active mode is not set - keep waiting diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index f60d60d9a4..dd49efd447 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -321,7 +321,7 @@ async def to_code(configs): frac = 2 elif frac > 0.19: frac = 4 - else: + elif frac != 0: frac = 8 displays = [ await cg.get_variable(display) for display in config[df.CONF_DISPLAYS] @@ -422,7 +422,7 @@ LVGL_SCHEMA = cv.All( ): lvalid.lv_font, cv.Optional(df.CONF_FULL_REFRESH, default=False): cv.boolean, cv.Optional(CONF_DRAW_ROUNDING, default=2): cv.positive_int, - cv.Optional(CONF_BUFFER_SIZE, default="100%"): cv.percentage, + cv.Optional(CONF_BUFFER_SIZE, default=0): cv.percentage, cv.Optional(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of( *df.LV_LOG_LEVELS, upper=True ), diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index 5fea9bfdb1..cc0f833ced 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -1,4 +1,5 @@ -from typing import Any, Callable +from collections.abc import Callable +from typing import Any from esphome import automation import esphome.codegen as cg @@ -20,7 +21,7 @@ from .defines import ( literal, static_cast, ) -from .lv_validation import lv_bool, lv_color, lv_image, opacity +from .lv_validation import lv_bool, lv_color, lv_image, lv_milliseconds, opacity from .lvcode import ( LVGL_COMP_ARG, UPDATE_EVENT, @@ -128,14 +129,14 @@ async def lvgl_is_paused(config, condition_id, template_arg, args): LVGL_SCHEMA.extend( { cv.Required(CONF_TIMEOUT): cv.templatable( - cv.positive_time_period_milliseconds + lv_milliseconds, ) } ), ) async def lvgl_is_idle(config, condition_id, template_arg, args): lvgl = config[CONF_LVGL_ID] - timeout = await cg.templatable(config[CONF_TIMEOUT], [], cg.uint32) + timeout = await lv_milliseconds.process(config[CONF_TIMEOUT]) async with LambdaContext(LVGL_COMP_ARG, return_type=cg.bool_) as context: lv_add(ReturnStatement(lvgl_comp.is_idle(timeout))) var = cg.new_Pvariable( diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 7783fb2321..baa9a19c51 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -519,8 +519,6 @@ CONF_UPDATE_ON_RELEASE = "update_on_release" CONF_VISIBLE_ROW_COUNT = "visible_row_count" CONF_WIDGET = "widget" CONF_WIDGETS = "widgets" -CONF_X = "x" -CONF_Y = "y" CONF_ZOOM = "zoom" # Keypad keys diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index 3755d35d27..92fe74eb52 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -1,5 +1,3 @@ -from typing import Union - import esphome.codegen as cg from esphome.components import image from esphome.components.color import CONF_HEX, ColorStruct, from_rgbw @@ -361,7 +359,7 @@ lv_image_list = LValidator( lv_bool = LValidator(cv.boolean, cg.bool_, retmapper=literal) -def lv_pct(value: Union[int, float]): +def lv_pct(value: int | float): if isinstance(value, float): value = int(value * 100) return literal(f"lv_pct({value})") diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index 67a87d24bf..7a5c35f896 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -1,5 +1,4 @@ import abc -from typing import Union from esphome import codegen as cg from esphome.config import Config @@ -75,7 +74,7 @@ class CodeContext(abc.ABC): code_context = None @abc.abstractmethod - def add(self, expression: Union[Expression, Statement]): + def add(self, expression: Expression | Statement): pass @staticmethod @@ -89,13 +88,13 @@ class CodeContext(abc.ABC): CodeContext.append(RawStatement("}")) @staticmethod - def append(expression: Union[Expression, Statement]): + def append(expression: Expression | Statement): if CodeContext.code_context is not None: CodeContext.code_context.add(expression) return expression def __init__(self): - self.previous: Union[CodeContext | None] = None + self.previous: CodeContext | None = None self.indent_level = 0 async def __aenter__(self): @@ -121,7 +120,7 @@ class MainContext(CodeContext): Code generation into the main() function """ - def add(self, expression: Union[Expression, Statement]): + def add(self, expression: Expression | Statement): return cg.add(self.indented_statement(expression)) @@ -144,7 +143,7 @@ class LambdaContext(CodeContext): self.capture = capture self.where = where - def add(self, expression: Union[Expression, Statement]): + def add(self, expression: Expression | Statement): self.code_list.append(self.indented_statement(expression)) return expression @@ -186,7 +185,7 @@ class LvContext(LambdaContext): async def __aexit__(self, exc_type, exc_val, exc_tb): await super().__aexit__(exc_type, exc_val, exc_tb) - def add(self, expression: Union[Expression, Statement]): + def add(self, expression: Expression | Statement): cg.add(expression) return expression @@ -303,7 +302,7 @@ lvgl_static = MockObj("LvglComponent", "::") # equivalent to cg.add() for the current code context -def lv_add(expression: Union[Expression, Statement]): +def lv_add(expression: Expression | Statement): return CodeContext.append(expression) diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 4c30d14e15..dd877df0f0 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -1,7 +1,7 @@ #include "esphome/core/defines.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "lvgl_hal.h" #include "lvgl_esphome.h" @@ -11,6 +11,8 @@ namespace esphome { namespace lvgl { static const char *const TAG = "lvgl"; +static const size_t MIN_BUFFER_FRAC = 8; + static const char *const EVENT_NAMES[] = { "NONE", "PRESSED", @@ -83,10 +85,14 @@ static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) { lv_event_code_t lv_api_event; // NOLINT lv_event_code_t lv_update_event; // NOLINT void LvglComponent::dump_config() { - ESP_LOGCONFIG(TAG, "LVGL:"); - ESP_LOGCONFIG(TAG, " Display width/height: %d x %d", this->disp_drv_.hor_res, this->disp_drv_.ver_res); - ESP_LOGCONFIG(TAG, " Rotation: %d", this->rotation); - ESP_LOGCONFIG(TAG, " Draw rounding: %d", (int) this->draw_rounding); + ESP_LOGCONFIG(TAG, + "LVGL:\n" + " Display width/height: %d x %d\n" + " Buffer size: %zu%%\n" + " Rotation: %d\n" + " Draw rounding: %d", + this->disp_drv_.hor_res, this->disp_drv_.ver_res, 100 / this->buffer_frac_, this->rotation, + (int) this->draw_rounding); } void LvglComponent::set_paused(bool paused, bool show_snow) { this->paused_ = paused; @@ -432,18 +438,28 @@ void LvglComponent::setup() { auto *display = this->displays_[0]; auto width = display->get_width(); auto height = display->get_height(); - size_t buffer_pixels = width * height / this->buffer_frac_; + auto frac = this->buffer_frac_; + if (frac == 0) + frac = 1; + size_t buffer_pixels = width * height / frac; auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8; void *buffer = nullptr; - if (this->buffer_frac_ >= 4) + if (this->buffer_frac_ >= MIN_BUFFER_FRAC / 2) buffer = malloc(buf_bytes); // NOLINT if (buffer == nullptr) buffer = lv_custom_mem_alloc(buf_bytes); // NOLINT + // if specific buffer size not set and can't get 100%, try for a smaller one + if (buffer == nullptr && this->buffer_frac_ == 0) { + frac = MIN_BUFFER_FRAC; + buffer_pixels /= MIN_BUFFER_FRAC; + buffer = lv_custom_mem_alloc(buf_bytes / MIN_BUFFER_FRAC); // NOLINT + } if (buffer == nullptr) { - this->mark_failed(); this->status_set_error("Memory allocation failure"); + this->mark_failed(); return; } + this->buffer_frac_ = frac; lv_disp_draw_buf_init(&this->draw_buf_, buffer, nullptr, buffer_pixels); this->disp_drv_.hor_res = width; this->disp_drv_.ver_res = height; @@ -453,8 +469,8 @@ void LvglComponent::setup() { if (this->rotation != display::DISPLAY_ROTATION_0_DEGREES) { this->rotate_buf_ = static_cast(lv_custom_mem_alloc(buf_bytes)); // NOLINT if (this->rotate_buf_ == nullptr) { - this->mark_failed(); this->status_set_error("Memory allocation failure"); + this->mark_failed(); return; } } diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index b51b6b8a85..fdc8750d1d 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -13,13 +13,15 @@ from esphome.const import ( CONF_TIME, CONF_TRIGGER_ID, CONF_TYPE, + CONF_X, + CONF_Y, ) from esphome.core import TimePeriod from esphome.core.config import StartupTrigger from esphome.schema_extractors import SCHEMA_EXTRACT from . import defines as df, lv_validation as lvalid -from .defines import CONF_TIME_FORMAT, CONF_X, CONF_Y, LV_GRAD_DIR +from .defines import CONF_TIME_FORMAT, LV_GRAD_DIR from .helpers import add_lv_use, requires_component, validate_printf from .lv_validation import lv_color, lv_font, lv_gradient, lv_image, opacity from .lvcode import LvglComponent, lv_event_t_ptr @@ -261,11 +263,13 @@ FLAG_LIST = cv.ensure_list(df.LvConstant("LV_OBJ_FLAG_", *df.OBJ_FLAGS).one_of) def part_schema(parts): """ Generate a schema for the various parts (e.g. main:, indicator:) of a widget type - :param parts: The parts to include in the schema + :param parts: The parts to include :return: The schema """ - return cv.Schema({cv.Optional(part): STATE_SCHEMA for part in parts}).extend( - STATE_SCHEMA + return ( + cv.Schema({cv.Optional(part): STATE_SCHEMA for part in parts}) + .extend(STATE_SCHEMA) + .extend(FLAG_SCHEMA) ) @@ -302,22 +306,18 @@ def base_update_schema(widget_type, parts): :param parts: The allowable parts to specify :return: """ - return ( - part_schema(parts) - .extend( - { - cv.Required(CONF_ID): cv.ensure_list( - cv.maybe_simple_value( - { - cv.Required(CONF_ID): cv.use_id(widget_type), - }, - key=CONF_ID, - ) - ), - cv.Optional(CONF_STATE): SET_STATE_SCHEMA, - } - ) - .extend(FLAG_SCHEMA) + return part_schema(parts).extend( + { + cv.Required(CONF_ID): cv.ensure_list( + cv.maybe_simple_value( + { + cv.Required(CONF_ID): cv.use_id(widget_type), + }, + key=CONF_ID, + ) + ), + cv.Optional(CONF_STATE): SET_STATE_SCHEMA, + } ) @@ -335,7 +335,6 @@ def obj_schema(widget_type: WidgetType): """ return ( part_schema(widget_type.parts) - .extend(FLAG_SCHEMA) .extend(LAYOUT_SCHEMA) .extend(ALIGN_TO_SCHEMA) .extend(automation_schema(widget_type.w_type)) @@ -357,8 +356,8 @@ ALIGN_TO_SCHEMA = { { cv.Required(CONF_ID): cv.use_id(lv_obj_t), cv.Required(df.CONF_ALIGN): df.ALIGN_ALIGNMENTS.one_of, - cv.Optional(df.CONF_X, default=0): lvalid.pixels_or_percent, - cv.Optional(df.CONF_Y, default=0): lvalid.pixels_or_percent, + cv.Optional(CONF_X, default=0): lvalid.pixels_or_percent, + cv.Optional(CONF_Y, default=0): lvalid.pixels_or_percent, } ) } diff --git a/esphome/components/lvgl/trigger.py b/esphome/components/lvgl/trigger.py index 283c9a5e56..2f8b454ec4 100644 --- a/esphome/components/lvgl/trigger.py +++ b/esphome/components/lvgl/trigger.py @@ -1,12 +1,17 @@ from esphome import automation import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_ON_BOOT, CONF_ON_VALUE, CONF_TRIGGER_ID +from esphome.const import ( + CONF_ID, + CONF_ON_BOOT, + CONF_ON_VALUE, + CONF_TRIGGER_ID, + CONF_X, + CONF_Y, +) from .defines import ( CONF_ALIGN, CONF_ALIGN_TO, - CONF_X, - CONF_Y, DIRECTIONS, LV_EVENT_MAP, LV_EVENT_TRIGGERS, diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index ccad45bdc6..9d53c0df26 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -1,5 +1,5 @@ import sys -from typing import Any, Union +from typing import Any from esphome import codegen as cg, config_validation as cv from esphome.config_validation import Invalid @@ -262,7 +262,7 @@ async def wait_for_widgets(): await FakeAwaitable(widgets_wait_generator()) -async def get_widgets(config: Union[dict, list], id: str = CONF_ID) -> list[Widget]: +async def get_widgets(config: dict | list, id: str = CONF_ID) -> list[Widget]: if not config: return [] if not isinstance(config, list): diff --git a/esphome/components/lvgl/widgets/canvas.py b/esphome/components/lvgl/widgets/canvas.py index 60812093d5..4fd81b6e4a 100644 --- a/esphome/components/lvgl/widgets/canvas.py +++ b/esphome/components/lvgl/widgets/canvas.py @@ -1,6 +1,14 @@ from esphome import automation, codegen as cg, config_validation as cv from esphome.components.display_menu_base import CONF_LABEL -from esphome.const import CONF_COLOR, CONF_HEIGHT, CONF_ID, CONF_TEXT, CONF_WIDTH +from esphome.const import ( + CONF_COLOR, + CONF_HEIGHT, + CONF_ID, + CONF_TEXT, + CONF_WIDTH, + CONF_X, + CONF_Y, +) from esphome.cpp_generator import Literal, MockObj from ..automation import action_to_code @@ -13,8 +21,6 @@ from ..defines import ( CONF_POINTS, CONF_SRC, CONF_START_ANGLE, - CONF_X, - CONF_Y, literal, ) from ..lv_validation import ( diff --git a/esphome/components/lvgl/widgets/line.py b/esphome/components/lvgl/widgets/line.py index 94fdfe2346..bd90edbefc 100644 --- a/esphome/components/lvgl/widgets/line.py +++ b/esphome/components/lvgl/widgets/line.py @@ -1,8 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv +from esphome.const import CONF_X, CONF_Y from esphome.core import Lambda -from ..defines import CONF_MAIN, CONF_X, CONF_Y, call_lambda +from ..defines import CONF_MAIN, call_lambda from ..lvcode import lv_add from ..schemas import point_schema from ..types import LvCompound, LvType diff --git a/esphome/components/lvgl/widgets/tabview.py b/esphome/components/lvgl/widgets/tabview.py index 1d18ddd259..42cf486e1c 100644 --- a/esphome/components/lvgl/widgets/tabview.py +++ b/esphome/components/lvgl/widgets/tabview.py @@ -24,6 +24,7 @@ from .obj import obj_spec CONF_TABVIEW = "tabview" CONF_TAB_STYLE = "tab_style" +CONF_CONTENT_STYLE = "content_style" lv_tab_t = LvType("lv_obj_t") @@ -39,6 +40,7 @@ TABVIEW_SCHEMA = cv.Schema( ) ), cv.Optional(CONF_TAB_STYLE): part_schema(buttonmatrix_spec.parts), + cv.Optional(CONF_CONTENT_STYLE): part_schema(obj_spec.parts), cv.Optional(CONF_POSITION, default="top"): DIRECTIONS.one_of, cv.Optional(CONF_SIZE, default="10%"): size, } @@ -79,6 +81,11 @@ class TabviewType(WidgetType): "tabview_btnmatrix", lv_obj_t, rhs=lv_expr.tabview_get_tab_btns(w.obj) ) as btnmatrix_obj: await set_obj_properties(Widget(btnmatrix_obj, obj_spec), button_style) + if content_style := config.get(CONF_CONTENT_STYLE): + with LocalVariable( + "tabview_content", lv_obj_t, rhs=lv_expr.tabview_get_content(w.obj) + ) as content_obj: + await set_obj_properties(Widget(content_obj, obj_spec), content_style) def obj_creator(self, parent: MockObjClass, config: dict): return lv_expr.call( diff --git a/esphome/components/m5stack_8angle/m5stack_8angle.cpp b/esphome/components/m5stack_8angle/m5stack_8angle.cpp index 6a584eddbc..416b903816 100644 --- a/esphome/components/m5stack_8angle/m5stack_8angle.cpp +++ b/esphome/components/m5stack_8angle/m5stack_8angle.cpp @@ -8,19 +8,19 @@ namespace m5stack_8angle { static const char *const TAG = "m5stack_8angle"; void M5Stack8AngleComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up M5STACK_8ANGLE..."); + ESP_LOGCONFIG(TAG, "Running setup"); i2c::ErrorCode err; err = this->read(nullptr, 0); if (err != i2c::NO_ERROR) { - ESP_LOGE(TAG, "I2C error %02X...", err); + ESP_LOGE(TAG, "I2C error %02X", err); this->mark_failed(); return; }; err = this->read_register(M5STACK_8ANGLE_REGISTER_FW_VERSION, &this->fw_version_, 1); if (err != i2c::NO_ERROR) { - ESP_LOGE(TAG, "I2C error %02X...", err); + ESP_LOGE(TAG, "I2C error %02X", err); this->mark_failed(); return; }; diff --git a/esphome/components/matrix_keypad/matrix_keypad.cpp b/esphome/components/matrix_keypad/matrix_keypad.cpp index 6cb4fc4f3c..43a20c49d1 100644 --- a/esphome/components/matrix_keypad/matrix_keypad.cpp +++ b/esphome/components/matrix_keypad/matrix_keypad.cpp @@ -97,8 +97,8 @@ void MatrixKeypad::loop() { } void MatrixKeypad::dump_config() { - ESP_LOGCONFIG(TAG, "Matrix Keypad:"); - ESP_LOGCONFIG(TAG, " Rows:"); + ESP_LOGCONFIG(TAG, "Matrix Keypad:\n" + " Rows:"); for (auto &pin : this->rows_) { LOG_PIN(" Pin: ", pin); } diff --git a/esphome/components/max17043/max17043.cpp b/esphome/components/max17043/max17043.cpp index 4a83320455..dc61babc7e 100644 --- a/esphome/components/max17043/max17043.cpp +++ b/esphome/components/max17043/max17043.cpp @@ -41,7 +41,7 @@ void MAX17043Component::update() { } void MAX17043Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MAX17043..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint16_t config_reg; if (this->write(&MAX17043_CONFIG, 1) != i2c::ERROR_OK) { @@ -76,7 +76,7 @@ void MAX17043Component::dump_config() { ESP_LOGCONFIG(TAG, "MAX17043:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with MAX17043 failed"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Battery Voltage", this->voltage_sensor_); diff --git a/esphome/components/max31855/max31855.cpp b/esphome/components/max31855/max31855.cpp index 445e086ef6..26fba428cc 100644 --- a/esphome/components/max31855/max31855.cpp +++ b/esphome/components/max31855/max31855.cpp @@ -1,5 +1,6 @@ #include "max31855.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -19,7 +20,7 @@ void MAX31855Sensor::update() { } void MAX31855Sensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up MAX31855Sensor '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); this->spi_setup(); } void MAX31855Sensor::dump_config() { diff --git a/esphome/components/max31856/max31856.cpp b/esphome/components/max31856/max31856.cpp index 6a4d34b430..c30e2e1a31 100644 --- a/esphome/components/max31856/max31856.cpp +++ b/esphome/components/max31856/max31856.cpp @@ -11,7 +11,7 @@ static const char *const TAG = "max31856"; // Based on Adafruit's library: https://github.com/adafruit/Adafruit_MAX31856 void MAX31856Sensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up MAX31856Sensor '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); this->spi_setup(); // assert on any fault @@ -23,8 +23,6 @@ void MAX31856Sensor::setup() { this->set_thermocouple_type_(); this->set_noise_filter_(); - - ESP_LOGCONFIG(TAG, "Completed setting up MAX31856Sensor '%s'...", this->name_.c_str()); } void MAX31856Sensor::dump_config() { @@ -97,28 +95,28 @@ bool MAX31856Sensor::has_fault_() { this->status_set_warning(); if ((faults & MAX31856_FAULT_CJRANGE) == MAX31856_FAULT_CJRANGE) { - ESP_LOGW(TAG, "Cold Junction Out-of-Range: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Cold Junction Out-of-Range: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_TCRANGE) == MAX31856_FAULT_TCRANGE) { - ESP_LOGW(TAG, "Thermocouple Out-of-Range: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Thermocouple Out-of-Range: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_CJHIGH) == MAX31856_FAULT_CJHIGH) { - ESP_LOGW(TAG, "Cold-Junction High Fault: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Cold-Junction High Fault: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_CJLOW) == MAX31856_FAULT_CJLOW) { - ESP_LOGW(TAG, "Cold-Junction Low Fault: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Cold-Junction Low Fault: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_TCHIGH) == MAX31856_FAULT_TCHIGH) { - ESP_LOGW(TAG, "Thermocouple Temperature High Fault: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Thermocouple Temperature High Fault: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_TCLOW) == MAX31856_FAULT_TCLOW) { - ESP_LOGW(TAG, "Thermocouple Temperature Low Fault: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Thermocouple Temperature Low Fault: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_OVUV) == MAX31856_FAULT_OVUV) { - ESP_LOGW(TAG, "Overvoltage or Undervoltage Input Fault: '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Overvoltage or Undervoltage Input Fault: '%s'", this->name_.c_str()); } if ((faults & MAX31856_FAULT_OPEN) == MAX31856_FAULT_OPEN) { - ESP_LOGW(TAG, "Thermocouple Open-Circuit Fault (possibly not connected): '%s'...", this->name_.c_str()); + ESP_LOGW(TAG, "Thermocouple Open-Circuit Fault (possibly not connected): '%s'", this->name_.c_str()); } return true; diff --git a/esphome/components/max31865/max31865.cpp b/esphome/components/max31865/max31865.cpp index 4749874ac7..4c9a4ae540 100644 --- a/esphome/components/max31865/max31865.cpp +++ b/esphome/components/max31865/max31865.cpp @@ -65,7 +65,7 @@ void MAX31865Sensor::update() { } void MAX31865Sensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up MAX31865Sensor '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); this->spi_setup(); // Build base configuration @@ -83,9 +83,11 @@ void MAX31865Sensor::dump_config() { LOG_SENSOR("", "MAX31865", this); LOG_PIN(" CS Pin: ", this->cs_); LOG_UPDATE_INTERVAL(this); - ESP_LOGCONFIG(TAG, " Reference Resistance: %.2fΩ", reference_resistance_); - ESP_LOGCONFIG(TAG, " RTD: %u-wire %.2fΩ", rtd_wires_, rtd_nominal_resistance_); - ESP_LOGCONFIG(TAG, " Mains Filter: %s", + ESP_LOGCONFIG(TAG, + " Reference Resistance: %.2fΩ\n" + " RTD: %u-wire %.2fΩ\n" + " Mains Filter: %s", + reference_resistance_, rtd_wires_, rtd_nominal_resistance_, (filter_ == FILTER_60HZ ? "60 Hz" : (filter_ == FILTER_50HZ ? "50 Hz" : "Unknown!"))); } diff --git a/esphome/components/max44009/max44009.cpp b/esphome/components/max44009/max44009.cpp index 6f12fb6583..6d1ce351d4 100644 --- a/esphome/components/max44009/max44009.cpp +++ b/esphome/components/max44009/max44009.cpp @@ -21,7 +21,7 @@ static const uint8_t MAX44009_ERROR_HIGH_BYTE = -30; static const uint8_t MAX44009_ERROR_LOW_BYTE = -31; void MAX44009Sensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up MAX44009..."); + ESP_LOGCONFIG(TAG, "Running setup"); bool state_ok = false; if (this->mode_ == MAX44009Mode::MAX44009_MODE_LOW_POWER) { state_ok = this->set_low_power_mode(); @@ -48,7 +48,7 @@ void MAX44009Sensor::dump_config() { ESP_LOGCONFIG(TAG, "MAX44009:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with MAX44009 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } diff --git a/esphome/components/max6675/max6675.cpp b/esphome/components/max6675/max6675.cpp index 1ec1d5ee53..a2881911f2 100644 --- a/esphome/components/max6675/max6675.cpp +++ b/esphome/components/max6675/max6675.cpp @@ -18,7 +18,7 @@ void MAX6675Sensor::update() { } void MAX6675Sensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up MAX6675Sensor '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); this->spi_setup(); } void MAX6675Sensor::dump_config() { diff --git a/esphome/components/max6956/max6956.cpp b/esphome/components/max6956/max6956.cpp index c2d9ba0175..5a1da9dc6f 100644 --- a/esphome/components/max6956/max6956.cpp +++ b/esphome/components/max6956/max6956.cpp @@ -20,7 +20,7 @@ const uint8_t MASK_CURRENT_PIN = 0x0F; * MAX6956 * **************************************/ void MAX6956::setup() { - ESP_LOGCONFIG(TAG, "Setting up MAX6956..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t configuration; if (!this->read_reg_(MAX6956_CONFIGURATION, &configuration)) { this->mark_failed(); @@ -146,8 +146,10 @@ void MAX6956::dump_config() { ESP_LOGCONFIG(TAG, "MAX6956"); if (brightness_mode_ == MAX6956CURRENTMODE::GLOBAL) { - ESP_LOGCONFIG(TAG, "current mode: global"); - ESP_LOGCONFIG(TAG, "global brightness: %u", global_brightness_); + ESP_LOGCONFIG(TAG, + "current mode: global\n" + "global brightness: %u", + global_brightness_); } else { ESP_LOGCONFIG(TAG, "current mode: segment"); } diff --git a/esphome/components/max7219/max7219.cpp b/esphome/components/max7219/max7219.cpp index d3cf6f5c48..3f78b35bbb 100644 --- a/esphome/components/max7219/max7219.cpp +++ b/esphome/components/max7219/max7219.cpp @@ -1,7 +1,7 @@ #include "max7219.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace max7219 { @@ -116,7 +116,7 @@ const uint8_t MAX7219_ASCII_TO_RAW[95] PROGMEM = { float MAX7219Component::get_setup_priority() const { return setup_priority::PROCESSOR; } void MAX7219Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MAX7219..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); this->buffer_ = new uint8_t[this->num_chips_ * 8]; // NOLINT for (uint8_t i = 0; i < this->num_chips_ * 8; i++) @@ -133,9 +133,11 @@ void MAX7219Component::setup() { this->send_to_all_(MAX7219_REGISTER_SHUTDOWN, 1); } void MAX7219Component::dump_config() { - ESP_LOGCONFIG(TAG, "MAX7219:"); - ESP_LOGCONFIG(TAG, " Number of Chips: %u", this->num_chips_); - ESP_LOGCONFIG(TAG, " Intensity: %u", this->intensity_); + ESP_LOGCONFIG(TAG, + "MAX7219:\n" + " Number of Chips: %u\n" + " Intensity: %u", + this->num_chips_, this->intensity_); LOG_PIN(" CS Pin: ", this->cs_); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/max7219digit/automation.h b/esphome/components/max7219digit/automation.h new file mode 100644 index 0000000000..02acebb109 --- /dev/null +++ b/esphome/components/max7219digit/automation.h @@ -0,0 +1,52 @@ +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/helpers.h" + +#include "max7219digit.h" + +namespace esphome { +namespace max7219digit { + +template class DisplayInvertAction : public Action, public Parented { + public: + TEMPLATABLE_VALUE(bool, state) + + void play(Ts... x) override { + bool state = this->state_.value(x...); + this->parent_->invert_on_off(state); + } +}; + +template class DisplayVisibilityAction : public Action, public Parented { + public: + TEMPLATABLE_VALUE(bool, state) + + void play(Ts... x) override { + bool state = this->state_.value(x...); + this->parent_->turn_on_off(state); + } +}; + +template class DisplayReverseAction : public Action, public Parented { + public: + TEMPLATABLE_VALUE(bool, state) + + void play(Ts... x) override { + bool state = this->state_.value(x...); + this->parent_->set_reverse(state); + } +}; + +template class DisplayIntensityAction : public Action, public Parented { + public: + TEMPLATABLE_VALUE(uint8_t, state) + + void play(Ts... x) override { + uint8_t state = this->state_.value(x...); + this->parent_->set_intensity(state); + } +}; + +} // namespace max7219digit +} // namespace esphome diff --git a/esphome/components/max7219digit/display.py b/esphome/components/max7219digit/display.py index 582d11bf4f..f195078c1a 100644 --- a/esphome/components/max7219digit/display.py +++ b/esphome/components/max7219digit/display.py @@ -1,7 +1,14 @@ +from esphome import automation import esphome.codegen as cg from esphome.components import display, spi import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_INTENSITY, CONF_LAMBDA, CONF_NUM_CHIPS +from esphome.const import ( + CONF_ID, + CONF_INTENSITY, + CONF_LAMBDA, + CONF_NUM_CHIPS, + CONF_STATE, +) CODEOWNERS = ["@rspaargaren"] DEPENDENCIES = ["spi"] @@ -17,6 +24,7 @@ CONF_REVERSE_ENABLE = "reverse_enable" CONF_NUM_CHIP_LINES = "num_chip_lines" CONF_CHIP_LINES_STYLE = "chip_lines_style" + integration_ns = cg.esphome_ns.namespace("max7219digit") ChipLinesStyle = integration_ns.enum("ChipLinesStyle") CHIP_LINES_STYLE = { @@ -99,3 +107,87 @@ async def to_code(config): config[CONF_LAMBDA], [(MAX7219ComponentRef, "it")], return_type=cg.void ) cg.add(var.set_writer(lambda_)) + + +DisplayInvertAction = max7219_ns.class_("DisplayInvertAction", automation.Action) +DisplayVisibilityAction = max7219_ns.class_( + "DisplayVisibilityAction", automation.Action +) +DisplayReverseAction = max7219_ns.class_("DisplayReverseAction", automation.Action) +DisplayIntensityAction = max7219_ns.class_("DisplayIntensityAction", automation.Action) + + +MAX7219_OFF_ACTION_SCHEMA = automation.maybe_simple_id( + { + cv.GenerateID(): cv.use_id(MAX7219Component), + cv.Optional(CONF_STATE, default=False): False, + } +) + +MAX7219_ON_ACTION_SCHEMA = automation.maybe_simple_id( + { + cv.GenerateID(): cv.use_id(MAX7219Component), + cv.Optional(CONF_STATE, default=True): True, + } +) + + +@automation.register_action( + "max7129digit.invert_off", DisplayInvertAction, MAX7219_OFF_ACTION_SCHEMA +) +@automation.register_action( + "max7129digit.invert_on", DisplayInvertAction, MAX7219_ON_ACTION_SCHEMA +) +async def max7129digit_invert_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + cg.add(var.set_state(config[CONF_STATE])) + return var + + +@automation.register_action( + "max7129digit.turn_off", DisplayVisibilityAction, MAX7219_OFF_ACTION_SCHEMA +) +@automation.register_action( + "max7129digit.turn_on", DisplayVisibilityAction, MAX7219_ON_ACTION_SCHEMA +) +async def max7129digit_visible_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + cg.add(var.set_state(config[CONF_STATE])) + return var + + +@automation.register_action( + "max7129digit.reverse_off", DisplayReverseAction, MAX7219_OFF_ACTION_SCHEMA +) +@automation.register_action( + "max7129digit.reverse_on", DisplayReverseAction, MAX7219_ON_ACTION_SCHEMA +) +async def max7129digit_reverse_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + cg.add(var.set_state(config[CONF_STATE])) + return var + + +MAX7219_INTENSITY_SCHEMA = cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(MAX7219Component), + cv.Optional(CONF_INTENSITY, default=15): cv.templatable( + cv.int_range(min=0, max=15) + ), + }, + key=CONF_INTENSITY, +) + + +@automation.register_action( + "max7129digit.intensity", DisplayIntensityAction, MAX7219_INTENSITY_SCHEMA +) +async def max7129digit_intensity_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_INTENSITY], args, cg.uint8) + cg.add(var.set_state(template_)) + return var diff --git a/esphome/components/max7219digit/max7219digit.cpp b/esphome/components/max7219digit/max7219digit.cpp index 154accd66f..1721dc80ce 100644 --- a/esphome/components/max7219digit/max7219digit.cpp +++ b/esphome/components/max7219digit/max7219digit.cpp @@ -1,8 +1,8 @@ #include "max7219digit.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" -#include "esphome/core/hal.h" #include "esphome/core/application.h" +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "max7219font.h" #include @@ -26,7 +26,7 @@ constexpr uint8_t MAX7219_DISPLAY_TEST = 0x01; float MAX7219Component::get_setup_priority() const { return setup_priority::PROCESSOR; } void MAX7219Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MAX7219_DIGITS..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); this->stepsleft_ = 0; for (int chip_line = 0; chip_line < this->num_chip_lines_; chip_line++) { @@ -50,15 +50,18 @@ void MAX7219Component::setup() { } void MAX7219Component::dump_config() { - ESP_LOGCONFIG(TAG, "MAX7219DIGIT:"); - ESP_LOGCONFIG(TAG, " Number of Chips: %u", this->num_chips_); - ESP_LOGCONFIG(TAG, " Number of Chips Lines: %u", this->num_chip_lines_); - ESP_LOGCONFIG(TAG, " Chips Lines Style : %u", this->chip_lines_style_); - ESP_LOGCONFIG(TAG, " Intensity: %u", this->intensity_); - ESP_LOGCONFIG(TAG, " Scroll Mode: %u", this->scroll_mode_); - ESP_LOGCONFIG(TAG, " Scroll Speed: %u", this->scroll_speed_); - ESP_LOGCONFIG(TAG, " Scroll Dwell: %u", this->scroll_dwell_); - ESP_LOGCONFIG(TAG, " Scroll Delay: %u", this->scroll_delay_); + ESP_LOGCONFIG(TAG, + "MAX7219DIGIT:\n" + " Number of Chips: %u\n" + " Number of Chips Lines: %u\n" + " Chips Lines Style : %u\n" + " Intensity: %u\n" + " Scroll Mode: %u\n" + " Scroll Speed: %u\n" + " Scroll Dwell: %u\n" + " Scroll Delay: %u", + this->num_chips_, this->num_chip_lines_, this->chip_lines_style_, this->intensity_, this->scroll_mode_, + this->scroll_speed_, this->scroll_dwell_, this->scroll_delay_); LOG_PIN(" CS Pin: ", this->cs_); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/max9611/max9611.cpp b/esphome/components/max9611/max9611.cpp index 70a94c4ad9..e61a30ab99 100644 --- a/esphome/components/max9611/max9611.cpp +++ b/esphome/components/max9611/max9611.cpp @@ -29,8 +29,9 @@ static const uint8_t SETUP_DELAY = 4; // Wait 2 integration periods. static const float VOUT_LSB = 14.0 / 1000.0; // 14mV/LSB static const float TEMP_LSB = 0.48; // 0.48C/LSB static const float MICRO_VOLTS_PER_VOLT = 1000000.0; + void MAX9611Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up max9611..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Perform dummy-read uint8_t value; this->read(&value, 1); @@ -41,18 +42,20 @@ void MAX9611Component::setup() { const uint8_t fast_mode_dat[] = {CONTROL_REGISTER_1_ADRR, MAX9611Multiplexer::MAX9611_MULTIPLEXER_FAST_MODE}; if (this->write(reinterpret_cast(&setup_dat), sizeof(setup_dat)) != ErrorCode::ERROR_OK) { - ESP_LOGE(TAG, "Failed to setup Max9611 during GAIN SET"); + ESP_LOGE(TAG, "GAIN SET failed"); return; } delay(SETUP_DELAY); if (this->write(reinterpret_cast(&fast_mode_dat), sizeof(fast_mode_dat)) != ErrorCode::ERROR_OK) { - ESP_LOGE(TAG, "Failed to setup Max9611 during FAST MODE SET"); + ESP_LOGE(TAG, "FAST MODE SET failed"); return; } } void MAX9611Component::dump_config() { - ESP_LOGCONFIG(TAG, "Dump Config max9611..."); - ESP_LOGCONFIG(TAG, " CSA Gain Register: %x", gain_); + ESP_LOGCONFIG(TAG, + "MAX9611:\n" + " CSA Gain Register: %x", + gain_); LOG_I2C_DEVICE(this); } void MAX9611Component::update() { @@ -62,7 +65,7 @@ void MAX9611Component::update() { // Just read the entire register map in a bulk read, faster than individually querying register. const ErrorCode read_result = this->read(register_map_, sizeof(register_map_)); if (write_result != ErrorCode::ERROR_OK || read_result != ErrorCode::ERROR_OK) { - ESP_LOGW(TAG, "MAX9611 Update FAILED!"); + ESP_LOGW(TAG, "Update failed"); return; } uint16_t csa_register = ((register_map_[CSA_DATA_BYTE_MSB_ADRR] << 8) | (register_map_[CSA_DATA_BYTE_LSB_ADRR])) >> 4; diff --git a/esphome/components/mcp23008/mcp23008.cpp b/esphome/components/mcp23008/mcp23008.cpp index 24b9a63f48..b93bec9e79 100644 --- a/esphome/components/mcp23008/mcp23008.cpp +++ b/esphome/components/mcp23008/mcp23008.cpp @@ -7,7 +7,7 @@ namespace mcp23008 { static const char *const TAG = "mcp23008"; void MCP23008::setup() { - ESP_LOGCONFIG(TAG, "Setting up MCP23008..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t iocon; if (!this->read_reg(mcp23x08_base::MCP23X08_IOCON, &iocon)) { this->mark_failed(); diff --git a/esphome/components/mcp23016/mcp23016.cpp b/esphome/components/mcp23016/mcp23016.cpp index 70df0dfb7b..17647e9915 100644 --- a/esphome/components/mcp23016/mcp23016.cpp +++ b/esphome/components/mcp23016/mcp23016.cpp @@ -8,7 +8,7 @@ namespace mcp23016 { static const char *const TAG = "mcp23016"; void MCP23016::setup() { - ESP_LOGCONFIG(TAG, "Setting up MCP23016..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t iocon; if (!this->read_reg_(MCP23016_IOCON0, &iocon)) { this->mark_failed(); diff --git a/esphome/components/mcp23017/mcp23017.cpp b/esphome/components/mcp23017/mcp23017.cpp index b82e750eaf..5c0c2c4703 100644 --- a/esphome/components/mcp23017/mcp23017.cpp +++ b/esphome/components/mcp23017/mcp23017.cpp @@ -7,7 +7,7 @@ namespace mcp23017 { static const char *const TAG = "mcp23017"; void MCP23017::setup() { - ESP_LOGCONFIG(TAG, "Setting up MCP23017..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t iocon; if (!this->read_reg(mcp23x17_base::MCP23X17_IOCONA, &iocon)) { this->mark_failed(); diff --git a/esphome/components/mcp23s08/mcp23s08.cpp b/esphome/components/mcp23s08/mcp23s08.cpp index 2f885743de..671506c79d 100644 --- a/esphome/components/mcp23s08/mcp23s08.cpp +++ b/esphome/components/mcp23s08/mcp23s08.cpp @@ -13,7 +13,7 @@ void MCP23S08::set_device_address(uint8_t device_addr) { } void MCP23S08::setup() { - ESP_LOGCONFIG(TAG, "Setting up MCP23S08..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); this->enable(); diff --git a/esphome/components/mcp23s17/mcp23s17.cpp b/esphome/components/mcp23s17/mcp23s17.cpp index ca4c28e0b4..1b922a8130 100644 --- a/esphome/components/mcp23s17/mcp23s17.cpp +++ b/esphome/components/mcp23s17/mcp23s17.cpp @@ -13,7 +13,7 @@ void MCP23S17::set_device_address(uint8_t device_addr) { } void MCP23S17::setup() { - ESP_LOGCONFIG(TAG, "Setting up MCP23S17..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); this->enable(); diff --git a/esphome/components/mcp3008/mcp3008.cpp b/esphome/components/mcp3008/mcp3008.cpp index aed48456b2..fb9bda35d0 100644 --- a/esphome/components/mcp3008/mcp3008.cpp +++ b/esphome/components/mcp3008/mcp3008.cpp @@ -11,7 +11,7 @@ static const char *const TAG = "mcp3008"; float MCP3008::get_setup_priority() const { return setup_priority::HARDWARE; } void MCP3008::setup() { - ESP_LOGCONFIG(TAG, "Setting up mcp3008"); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); } diff --git a/esphome/components/mcp3008/sensor/mcp3008_sensor.cpp b/esphome/components/mcp3008/sensor/mcp3008_sensor.cpp index df2a8735f8..81eb0a812f 100644 --- a/esphome/components/mcp3008/sensor/mcp3008_sensor.cpp +++ b/esphome/components/mcp3008/sensor/mcp3008_sensor.cpp @@ -10,9 +10,11 @@ static const char *const TAG = "mcp3008.sensor"; float MCP3008Sensor::get_setup_priority() const { return setup_priority::DATA; } void MCP3008Sensor::dump_config() { - ESP_LOGCONFIG(TAG, "MCP3008Sensor:"); - ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); - ESP_LOGCONFIG(TAG, " Reference Voltage: %.2fV", this->reference_voltage_); + ESP_LOGCONFIG(TAG, + "MCP3008Sensor:\n" + " Pin: %u\n" + " Reference Voltage: %.2fV", + this->pin_, this->reference_voltage_); } float MCP3008Sensor::sample() { diff --git a/esphome/components/mcp3204/mcp3204.cpp b/esphome/components/mcp3204/mcp3204.cpp index 283df4ccdc..1f956612d7 100644 --- a/esphome/components/mcp3204/mcp3204.cpp +++ b/esphome/components/mcp3204/mcp3204.cpp @@ -9,7 +9,7 @@ static const char *const TAG = "mcp3204"; float MCP3204::get_setup_priority() const { return setup_priority::HARDWARE; } void MCP3204::setup() { - ESP_LOGCONFIG(TAG, "Setting up mcp3204"); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); } diff --git a/esphome/components/mcp4461/mcp4461.cpp b/esphome/components/mcp4461/mcp4461.cpp index 5393241281..39127a6c04 100644 --- a/esphome/components/mcp4461/mcp4461.cpp +++ b/esphome/components/mcp4461/mcp4461.cpp @@ -1,7 +1,7 @@ #include "mcp4461.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" namespace esphome { namespace mcp4461 { @@ -10,7 +10,7 @@ static const char *const TAG = "mcp4461"; constexpr uint8_t EEPROM_WRITE_TIMEOUT_MS = 10; void Mcp4461Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up mcp4461 using address (0x%02X)...", this->address_); + ESP_LOGCONFIG(TAG, "Running setup for address 0x%02X", this->address_); auto err = this->write(nullptr, 0); if (err != i2c::ERROR_OK) { this->error_code_ = MCP4461_STATUS_I2C_ERROR; diff --git a/esphome/components/mcp4725/mcp4725.cpp b/esphome/components/mcp4725/mcp4725.cpp index 2cb19282b6..8b2f8524d8 100644 --- a/esphome/components/mcp4725/mcp4725.cpp +++ b/esphome/components/mcp4725/mcp4725.cpp @@ -7,7 +7,7 @@ namespace mcp4725 { static const char *const TAG = "mcp4725"; void MCP4725::setup() { - ESP_LOGCONFIG(TAG, "Setting up MCP4725 (0x%02X)...", this->address_); + ESP_LOGCONFIG(TAG, "Running setup for address 0x%02X", this->address_); auto err = this->write(nullptr, 0); if (err != i2c::ERROR_OK) { this->error_code_ = COMMUNICATION_FAILED; @@ -20,7 +20,7 @@ void MCP4725::dump_config() { LOG_I2C_DEVICE(this); if (this->error_code_ == COMMUNICATION_FAILED) { - ESP_LOGE(TAG, "Communication with MCP4725 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } diff --git a/esphome/components/mcp4728/mcp4728.cpp b/esphome/components/mcp4728/mcp4728.cpp index 1a8568a21c..7b2b43d4d8 100644 --- a/esphome/components/mcp4728/mcp4728.cpp +++ b/esphome/components/mcp4728/mcp4728.cpp @@ -9,7 +9,7 @@ namespace mcp4728 { static const char *const TAG = "mcp4728"; void MCP4728Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MCP4728 (0x%02X)...", this->address_); + ESP_LOGCONFIG(TAG, "Running setup for address 0x%02X", this->address_); auto err = this->write(nullptr, 0); if (err != i2c::ERROR_OK) { this->mark_failed(); @@ -21,7 +21,7 @@ void MCP4728Component::dump_config() { ESP_LOGCONFIG(TAG, "MCP4728:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with MCP4728 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } diff --git a/esphome/components/mcp9600/mcp9600.cpp b/esphome/components/mcp9600/mcp9600.cpp index b56c838f78..16c19326f2 100644 --- a/esphome/components/mcp9600/mcp9600.cpp +++ b/esphome/components/mcp9600/mcp9600.cpp @@ -28,7 +28,7 @@ static const uint8_t MCP9600_REGISTER_ALERT4_LIMIT = 0x13; static const uint8_t MCP9600_REGISTER_DEVICE_ID = 0x20; void MCP9600Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MCP9600..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint16_t dev_id = 0; this->read_byte_16(MCP9600_REGISTER_DEVICE_ID, &dev_id); diff --git a/esphome/components/mcp9808/mcp9808.cpp b/esphome/components/mcp9808/mcp9808.cpp index 626fcd540d..02ddc1aceb 100644 --- a/esphome/components/mcp9808/mcp9808.cpp +++ b/esphome/components/mcp9808/mcp9808.cpp @@ -18,18 +18,18 @@ static const uint8_t MCP9808_AMBIENT_TEMP_NEGATIVE = 0x10; static const char *const TAG = "mcp9808"; void MCP9808Sensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up %s...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); uint16_t manu = 0; if (!this->read_byte_16(MCP9808_REG_MANUF_ID, &manu) || manu != MCP9808_MANUF_ID) { this->mark_failed(); - ESP_LOGE(TAG, "%s manufacuturer id failed, device returned %X", this->name_.c_str(), manu); + ESP_LOGE(TAG, "Incorrect manufacturer ID (%X) for '%s'", manu, this->name_.c_str()); return; } uint16_t dev_id = 0; if (!this->read_byte_16(MCP9808_REG_DEVICE_ID, &dev_id) || dev_id != MCP9808_DEV_ID) { this->mark_failed(); - ESP_LOGE(TAG, "%s device id failed, device returned %X", this->name_.c_str(), dev_id); + ESP_LOGE(TAG, "Incorrect device ID (%X) for '%s'", dev_id, this->name_.c_str()); return; } } @@ -37,7 +37,7 @@ void MCP9808Sensor::dump_config() { ESP_LOGCONFIG(TAG, "%s:", this->name_.c_str()); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with %s failed!", this->name_.c_str()); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL_FOR, this->name_.c_str()); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this); diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp index ffc668e218..06ca99b402 100644 --- a/esphome/components/mdns/mdns_component.cpp +++ b/esphome/components/mdns/mdns_component.cpp @@ -59,6 +59,8 @@ void MDNSComponent::compile_records_() { service.txt_records.push_back({"network", "wifi"}); #elif defined(USE_ETHERNET) service.txt_records.push_back({"network", "ethernet"}); +#elif defined(USE_OPENTHREAD) + service.txt_records.push_back({"network", "thread"}); #endif #ifdef USE_API_NOISE @@ -117,8 +119,10 @@ void MDNSComponent::compile_records_() { } void MDNSComponent::dump_config() { - ESP_LOGCONFIG(TAG, "mDNS:"); - ESP_LOGCONFIG(TAG, " Hostname: %s", this->hostname_.c_str()); + ESP_LOGCONFIG(TAG, + "mDNS:\n" + " Hostname: %s", + this->hostname_.c_str()); ESP_LOGV(TAG, " Services:"); for (const auto &service : this->services_) { ESP_LOGV(TAG, " - %s, %s, %d", service.service_type.c_str(), service.proto.c_str(), @@ -130,6 +134,8 @@ void MDNSComponent::dump_config() { } } +std::vector MDNSComponent::get_services() { return this->services_; } + } // namespace mdns } // namespace esphome #endif diff --git a/esphome/components/mdns/mdns_component.h b/esphome/components/mdns/mdns_component.h index 9eb2ba11d0..93a16f40d2 100644 --- a/esphome/components/mdns/mdns_component.h +++ b/esphome/components/mdns/mdns_component.h @@ -33,10 +33,12 @@ class MDNSComponent : public Component { #if (defined(USE_ESP8266) || defined(USE_RP2040)) && defined(USE_ARDUINO) void loop() override; #endif - float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + float get_setup_priority() const override { return setup_priority::AFTER_CONNECTION; } void add_extra_service(MDNSService service) { services_extra_.push_back(std::move(service)); } + std::vector get_services(); + void on_shutdown() override; protected: diff --git a/esphome/components/mdns/mdns_esp32.cpp b/esphome/components/mdns/mdns_esp32.cpp index fed18d3630..ffd86afec1 100644 --- a/esphome/components/mdns/mdns_esp32.cpp +++ b/esphome/components/mdns/mdns_esp32.cpp @@ -17,7 +17,7 @@ void MDNSComponent::setup() { esp_err_t err = mdns_init(); if (err != ESP_OK) { - ESP_LOGW(TAG, "mDNS init failed: %s", esp_err_to_name(err)); + ESP_LOGW(TAG, "Init failed: %s", esp_err_to_name(err)); this->mark_failed(); return; } @@ -45,7 +45,7 @@ void MDNSComponent::setup() { } if (err != ESP_OK) { - ESP_LOGW(TAG, "Failed to register mDNS service %s: %s", service.service_type.c_str(), esp_err_to_name(err)); + ESP_LOGW(TAG, "Failed to register service %s: %s", service.service_type.c_str(), esp_err_to_name(err)); } } } diff --git a/esphome/components/media_player/__init__.py b/esphome/components/media_player/__init__.py index 2f5fe0c03e..ef76419de3 100644 --- a/esphome/components/media_player/__init__.py +++ b/esphome/components/media_player/__init__.py @@ -103,6 +103,7 @@ async def register_media_player(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_media_player(var)) + CORE.register_platform_component("media_player", var) await setup_media_player_core_(var, config) diff --git a/esphome/components/micro_wake_word/micro_wake_word.cpp b/esphome/components/micro_wake_word/micro_wake_word.cpp index a44348fdc9..201d956a37 100644 --- a/esphome/components/micro_wake_word/micro_wake_word.cpp +++ b/esphome/components/micro_wake_word/micro_wake_word.cpp @@ -72,7 +72,7 @@ void MicroWakeWord::dump_config() { } void MicroWakeWord::setup() { - ESP_LOGCONFIG(TAG, "Setting up microWakeWord..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->frontend_config_.window.size_ms = FEATURE_DURATION_MS; this->frontend_config_.window.step_size_ms = this->features_step_size_; diff --git a/esphome/components/micro_wake_word/streaming_model.cpp b/esphome/components/micro_wake_word/streaming_model.cpp index 38b88557e6..31341bba0d 100644 --- a/esphome/components/micro_wake_word/streaming_model.cpp +++ b/esphome/components/micro_wake_word/streaming_model.cpp @@ -11,15 +11,19 @@ namespace esphome { namespace micro_wake_word { void WakeWordModel::log_model_config() { - ESP_LOGCONFIG(TAG, " - Wake Word: %s", this->wake_word_.c_str()); - ESP_LOGCONFIG(TAG, " Probability cutoff: %.2f", this->probability_cutoff_ / 255.0f); - ESP_LOGCONFIG(TAG, " Sliding window size: %d", this->sliding_window_size_); + ESP_LOGCONFIG(TAG, + " - Wake Word: %s\n" + " Probability cutoff: %.2f\n" + " Sliding window size: %d", + this->wake_word_.c_str(), this->probability_cutoff_ / 255.0f, this->sliding_window_size_); } void VADModel::log_model_config() { - ESP_LOGCONFIG(TAG, " - VAD Model"); - ESP_LOGCONFIG(TAG, " Probability cutoff: %.2f", this->probability_cutoff_ / 255.0f); - ESP_LOGCONFIG(TAG, " Sliding window size: %d", this->sliding_window_size_); + ESP_LOGCONFIG(TAG, + " - VAD Model\n" + " Probability cutoff: %.2f\n" + " Sliding window size: %d", + this->probability_cutoff_ / 255.0f, this->sliding_window_size_); } bool StreamingModel::load_model_() { diff --git a/esphome/components/micronova/micronova.h b/esphome/components/micronova/micronova.h index aebef277e5..fc68d941cf 100644 --- a/esphome/components/micronova/micronova.h +++ b/esphome/components/micronova/micronova.h @@ -1,10 +1,10 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/components/uart/uart.h" -#include "esphome/core/log.h" +#include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include diff --git a/esphome/components/mics_4514/mics_4514.cpp b/esphome/components/mics_4514/mics_4514.cpp index ed2fc6c826..3a2cf22914 100644 --- a/esphome/components/mics_4514/mics_4514.cpp +++ b/esphome/components/mics_4514/mics_4514.cpp @@ -12,11 +12,11 @@ static const uint8_t SENSOR_REGISTER = 0x04; static const uint8_t POWER_MODE_REGISTER = 0x0a; void MICS4514Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MICS 4514..."); + ESP_LOGCONFIG(TAG, "Running 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, sensors will have data after 3 minutes"); power_mode = 0x01; this->write_register(POWER_MODE_REGISTER, &power_mode, 1); delay(100); // NOLINT diff --git a/esphome/components/midea/air_conditioner.cpp b/esphome/components/midea/air_conditioner.cpp index 247aea0488..170a2f6a40 100644 --- a/esphome/components/midea/air_conditioner.cpp +++ b/esphome/components/midea/air_conditioner.cpp @@ -1,7 +1,7 @@ #ifdef USE_ARDUINO -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "air_conditioner.h" #include "ac_adapter.h" #include @@ -103,10 +103,12 @@ ClimateTraits AirConditioner::traits() { } void AirConditioner::dump_config() { - ESP_LOGCONFIG(Constants::TAG, "MideaDongle:"); - ESP_LOGCONFIG(Constants::TAG, " [x] Period: %dms", this->base_.getPeriod()); - ESP_LOGCONFIG(Constants::TAG, " [x] Response timeout: %dms", this->base_.getTimeout()); - ESP_LOGCONFIG(Constants::TAG, " [x] Request attempts: %d", this->base_.getNumAttempts()); + ESP_LOGCONFIG(Constants::TAG, + "MideaDongle:\n" + " [x] Period: %dms\n" + " [x] Response timeout: %dms\n" + " [x] Request attempts: %d", + this->base_.getPeriod(), this->base_.getTimeout(), this->base_.getNumAttempts()); #ifdef USE_REMOTE_TRANSMITTER ESP_LOGCONFIG(Constants::TAG, " [x] Using RemoteTransmitter"); #endif diff --git a/esphome/components/midea_ir/midea_ir.cpp b/esphome/components/midea_ir/midea_ir.cpp index aa5e2b46f5..c269b2f7d9 100644 --- a/esphome/components/midea_ir/midea_ir.cpp +++ b/esphome/components/midea_ir/midea_ir.cpp @@ -1,7 +1,7 @@ #include "midea_ir.h" #include "midea_data.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/components/coolix/coolix.h" namespace esphome { diff --git a/esphome/components/mipi_spi/mipi_spi.cpp b/esphome/components/mipi_spi/mipi_spi.cpp index 2d393ac349..962575477d 100644 --- a/esphome/components/mipi_spi/mipi_spi.cpp +++ b/esphome/components/mipi_spi/mipi_spi.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace mipi_spi { void MipiSpi::setup() { - ESP_LOGCONFIG(TAG, "Setting up MIPI SPI"); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); if (this->dc_pin_ != nullptr) { this->dc_pin_->setup(); @@ -447,21 +447,28 @@ void MipiSpi::write_command_(uint8_t cmd, const uint8_t *bytes, size_t len) { } void MipiSpi::dump_config() { - ESP_LOGCONFIG(TAG, "MIPI_SPI Display"); - ESP_LOGCONFIG(TAG, " Model: %s", this->model_); - ESP_LOGCONFIG(TAG, " Width: %u", this->width_); - ESP_LOGCONFIG(TAG, " Height: %u", this->height_); + ESP_LOGCONFIG(TAG, + "MIPI_SPI Display\n" + " Model: %s\n" + " Width: %u\n" + " Height: %u", + this->model_, this->width_, this->height_); if (this->offset_width_ != 0) ESP_LOGCONFIG(TAG, " Offset width: %u", this->offset_width_); if (this->offset_height_ != 0) ESP_LOGCONFIG(TAG, " Offset height: %u", this->offset_height_); - ESP_LOGCONFIG(TAG, " Swap X/Y: %s", YESNO(this->madctl_ & MADCTL_MV)); - ESP_LOGCONFIG(TAG, " Mirror X: %s", YESNO(this->madctl_ & (MADCTL_MX | MADCTL_XFLIP))); - ESP_LOGCONFIG(TAG, " Mirror Y: %s", YESNO(this->madctl_ & (MADCTL_MY | MADCTL_YFLIP))); - ESP_LOGCONFIG(TAG, " Color depth: %d bits", this->color_depth_ == display::COLOR_BITNESS_565 ? 16 : 8); - ESP_LOGCONFIG(TAG, " Invert colors: %s", YESNO(this->invert_colors_)); - ESP_LOGCONFIG(TAG, " Color order: %s", this->madctl_ & MADCTL_BGR ? "BGR" : "RGB"); - ESP_LOGCONFIG(TAG, " Pixel mode: %s", this->pixel_mode_ == PIXEL_MODE_18 ? "18bit" : "16bit"); + ESP_LOGCONFIG(TAG, + " Swap X/Y: %s\n" + " Mirror X: %s\n" + " Mirror Y: %s\n" + " Color depth: %d bits\n" + " Invert colors: %s\n" + " Color order: %s\n" + " Pixel mode: %s", + YESNO(this->madctl_ & MADCTL_MV), YESNO(this->madctl_ & (MADCTL_MX | MADCTL_XFLIP)), + YESNO(this->madctl_ & (MADCTL_MY | MADCTL_YFLIP)), + this->color_depth_ == display::COLOR_BITNESS_565 ? 16 : 8, YESNO(this->invert_colors_), + this->madctl_ & MADCTL_BGR ? "BGR" : "RGB", this->pixel_mode_ == PIXEL_MODE_18 ? "18bit" : "16bit"); if (this->brightness_.has_value()) ESP_LOGCONFIG(TAG, " Brightness: %u", this->brightness_.value()); if (this->spi_16_) @@ -472,9 +479,11 @@ void MipiSpi::dump_config() { LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_PIN(" DC Pin: ", this->dc_pin_); - ESP_LOGCONFIG(TAG, " SPI Mode: %d", this->mode_); - ESP_LOGCONFIG(TAG, " SPI Data rate: %dMHz", static_cast(this->data_rate_ / 1000000)); - ESP_LOGCONFIG(TAG, " SPI Bus width: %d", this->bus_width_); + ESP_LOGCONFIG(TAG, + " SPI Mode: %d\n" + " SPI Data rate: %dMHz\n" + " SPI Bus width: %d", + this->mode_, static_cast(this->data_rate_ / 1000000), this->bus_width_); } } // namespace mipi_spi diff --git a/esphome/components/mixer/speaker/mixer_speaker.cpp b/esphome/components/mixer/speaker/mixer_speaker.cpp index 8e480dd49b..fc0517c7be 100644 --- a/esphome/components/mixer/speaker/mixer_speaker.cpp +++ b/esphome/components/mixer/speaker/mixer_speaker.cpp @@ -43,8 +43,10 @@ enum MixerEventGroupBits : uint32_t { }; void SourceSpeaker::dump_config() { - ESP_LOGCONFIG(TAG, "Mixer Source Speaker"); - ESP_LOGCONFIG(TAG, " Buffer Duration: %" PRIu32 " ms", this->buffer_duration_ms_); + ESP_LOGCONFIG(TAG, + "Mixer Source Speaker\n" + " Buffer Duration: %" PRIu32 " ms", + this->buffer_duration_ms_); if (this->timeout_ms_.has_value()) { ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 " ms", this->timeout_ms_.value()); } else { @@ -291,8 +293,10 @@ void SourceSpeaker::duck_samples(int16_t *input_buffer, uint32_t input_samples_t } void MixerSpeaker::dump_config() { - ESP_LOGCONFIG(TAG, "Speaker Mixer:"); - ESP_LOGCONFIG(TAG, " Number of output channels: %u", this->output_channels_); + ESP_LOGCONFIG(TAG, + "Speaker Mixer:\n" + " Number of output channels: %u", + this->output_channels_); } void MixerSpeaker::setup() { diff --git a/esphome/components/mlx90393/sensor_mlx90393.cpp b/esphome/components/mlx90393/sensor_mlx90393.cpp index 46fe68fab0..96749cd378 100644 --- a/esphome/components/mlx90393/sensor_mlx90393.cpp +++ b/esphome/components/mlx90393/sensor_mlx90393.cpp @@ -103,7 +103,7 @@ bool MLX90393Cls::apply_all_settings_() { } void MLX90393Cls::setup() { - ESP_LOGCONFIG(TAG, "Setting up MLX90393..."); + ESP_LOGCONFIG(TAG, "Running setup"); // note the two arguments A0 and A1 which are used to construct an i2c address // we can hard-code these because we never actually use the constructed address // see the transceive function above, which uses the address from I2CComponent @@ -122,7 +122,7 @@ void MLX90393Cls::dump_config() { LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with MLX90393 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); return; } LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/mlx90614/mlx90614.cpp b/esphome/components/mlx90614/mlx90614.cpp index f681f3cc7e..afc565d38b 100644 --- a/esphome/components/mlx90614/mlx90614.cpp +++ b/esphome/components/mlx90614/mlx90614.cpp @@ -1,6 +1,7 @@ #include "mlx90614.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -27,9 +28,9 @@ static const uint8_t MLX90614_ID4 = 0x3F; static const char *const TAG = "mlx90614"; void MLX90614Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MLX90614..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->write_emissivity_()) { - ESP_LOGE(TAG, "Communication with MLX90614 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); return; } @@ -79,7 +80,7 @@ void MLX90614Component::dump_config() { ESP_LOGCONFIG(TAG, "MLX90614:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with MLX90614 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Ambient", this->ambient_sensor_); diff --git a/esphome/components/mmc5603/mmc5603.cpp b/esphome/components/mmc5603/mmc5603.cpp index 6fbf4810f2..86b1b23c15 100644 --- a/esphome/components/mmc5603/mmc5603.cpp +++ b/esphome/components/mmc5603/mmc5603.cpp @@ -31,7 +31,7 @@ static const uint8_t MMC56X3_CTRL2_REG = 0x1D; static const uint8_t MMC5603_ODR_REG = 0x1A; void MMC5603Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MMC5603..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t id = 0; if (!this->read_byte(MMC56X3_PRODUCT_ID, &id)) { this->error_code_ = COMMUNICATION_FAILED; @@ -79,7 +79,7 @@ void MMC5603Component::dump_config() { ESP_LOGCONFIG(TAG, "MMC5603:"); LOG_I2C_DEVICE(this); if (this->error_code_ == COMMUNICATION_FAILED) { - ESP_LOGE(TAG, "Communication with MMC5603 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } else if (this->error_code_ == ID_REGISTERS) { ESP_LOGE(TAG, "The ID registers don't match - Is this really an MMC5603?"); } diff --git a/esphome/components/mmc5983/mmc5983.cpp b/esphome/components/mmc5983/mmc5983.cpp index 5b045ae38b..d5394da618 100644 --- a/esphome/components/mmc5983/mmc5983.cpp +++ b/esphome/components/mmc5983/mmc5983.cpp @@ -67,7 +67,7 @@ void MMC5983Component::update() { } void MMC5983Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MMC5983..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Verify product id. const uint8_t mmc5983_product_id = 0x30; diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index 80c2ffe3d6..c2efa93fae 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -1,7 +1,7 @@ #include "modbus.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace modbus { @@ -90,7 +90,7 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) { } else { // data starts at 2 and length is 4 for read registers commands - if (this->role == ModbusRole::SERVER && (function_code == 0x3 || function_code == 0x4)) { + if (this->role == ModbusRole::SERVER && (function_code == 0x1 || function_code == 0x3 || function_code == 0x4)) { data_offset = 2; data_len = 4; } @@ -165,8 +165,10 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) { void Modbus::dump_config() { ESP_LOGCONFIG(TAG, "Modbus:"); LOG_PIN(" Flow Control Pin: ", this->flow_control_pin_); - ESP_LOGCONFIG(TAG, " Send Wait Time: %d ms", this->send_wait_time_); - ESP_LOGCONFIG(TAG, " CRC Disabled: %s", YESNO(this->disable_crc_)); + ESP_LOGCONFIG(TAG, + " Send Wait Time: %d ms\n" + " CRC Disabled: %s", + this->send_wait_time_, YESNO(this->disable_crc_)); } float Modbus::get_setup_priority() const { // After UART bus diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index 3f487abc94..48ff868087 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -346,10 +346,12 @@ size_t ModbusController::create_register_ranges_() { } void ModbusController::dump_config() { - ESP_LOGCONFIG(TAG, "ModbusController:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); - ESP_LOGCONFIG(TAG, " Max Command Retries: %d", this->max_cmd_retries_); - ESP_LOGCONFIG(TAG, " Offline Skip Updates: %d", this->offline_skip_updates_); + ESP_LOGCONFIG(TAG, + "ModbusController:\n" + " Address: 0x%02X\n" + " Max Command Retries: %d\n" + " Offline Skip Updates: %d", + this->address_, this->max_cmd_retries_, this->offline_skip_updates_); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE ESP_LOGCONFIG(TAG, "sensormap"); for (auto &it : this->sensorset_) { diff --git a/esphome/components/modbus_controller/output/modbus_output.cpp b/esphome/components/modbus_controller/output/modbus_output.cpp index f0f6e64f10..45e786a704 100644 --- a/esphome/components/modbus_controller/output/modbus_output.cpp +++ b/esphome/components/modbus_controller/output/modbus_output.cpp @@ -52,9 +52,11 @@ void ModbusFloatOutput::write_state(float value) { void ModbusFloatOutput::dump_config() { ESP_LOGCONFIG(TAG, "Modbus Float Output:"); LOG_FLOAT_OUTPUT(this); - ESP_LOGCONFIG(TAG, " Device start address: 0x%X", this->start_address); - ESP_LOGCONFIG(TAG, " Register count: %d", this->register_count); - ESP_LOGCONFIG(TAG, " Value type: %d", static_cast(this->sensor_value_type)); + ESP_LOGCONFIG(TAG, + " Device start address: 0x%X\n" + " Register count: %d\n" + " Value type: %d", + this->start_address, this->register_count, static_cast(this->sensor_value_type)); } // ModbusBinaryOutput @@ -102,9 +104,11 @@ void ModbusBinaryOutput::write_state(bool state) { void ModbusBinaryOutput::dump_config() { ESP_LOGCONFIG(TAG, "Modbus Binary Output:"); LOG_BINARY_OUTPUT(this); - ESP_LOGCONFIG(TAG, " Device start address: 0x%X", this->start_address); - ESP_LOGCONFIG(TAG, " Register count: %d", this->register_count); - ESP_LOGCONFIG(TAG, " Value type: %d", static_cast(this->sensor_value_type)); + ESP_LOGCONFIG(TAG, + " Device start address: 0x%X\n" + " Register count: %d\n" + " Value type: %d", + this->start_address, this->register_count, static_cast(this->sensor_value_type)); } } // namespace modbus_controller diff --git a/esphome/components/modbus_controller/switch/__init__.py b/esphome/components/modbus_controller/switch/__init__.py index 258d87fd25..e325e6198e 100644 --- a/esphome/components/modbus_controller/switch/__init__.py +++ b/esphome/components/modbus_controller/switch/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg from esphome.components import switch import esphome.config_validation as cv -from esphome.const import CONF_ADDRESS, CONF_ID +from esphome.const import CONF_ADDRESS, CONF_ASSUMED_STATE, CONF_ID from .. import ( MODBUS_REGISTER_TYPE, @@ -36,6 +36,7 @@ CONFIG_SCHEMA = cv.All( .extend(ModbusItemBaseSchema) .extend( { + cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean, cv.Optional(CONF_REGISTER_TYPE): cv.enum(MODBUS_REGISTER_TYPE), cv.Optional(CONF_USE_WRITE_MULTIPLE, default=False): cv.boolean, cv.Optional(CONF_WRITE_LAMBDA): cv.returning_lambda, @@ -62,7 +63,10 @@ async def to_code(config): paren = await cg.get_variable(config[CONF_MODBUS_CONTROLLER_ID]) cg.add(var.set_parent(paren)) cg.add(var.set_use_write_mutiple(config[CONF_USE_WRITE_MULTIPLE])) - cg.add(paren.add_sensor_item(var)) + assumed_state = config[CONF_ASSUMED_STATE] + cg.add(var.set_assumed_state(assumed_state)) + if not assumed_state: + cg.add(paren.add_sensor_item(var)) if CONF_WRITE_LAMBDA in config: template_ = await cg.process_lambda( config[CONF_WRITE_LAMBDA], diff --git a/esphome/components/modbus_controller/switch/modbus_switch.cpp b/esphome/components/modbus_controller/switch/modbus_switch.cpp index b729e2659f..21c4c1718d 100644 --- a/esphome/components/modbus_controller/switch/modbus_switch.cpp +++ b/esphome/components/modbus_controller/switch/modbus_switch.cpp @@ -19,6 +19,10 @@ void ModbusSwitch::setup() { } void ModbusSwitch::dump_config() { LOG_SWITCH(TAG, "Modbus Controller Switch", this); } +void ModbusSwitch::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; } + +bool ModbusSwitch::assumed_state() { return this->assumed_state_; } + void ModbusSwitch::parse_and_publish(const std::vector &data) { bool value = false; switch (this->register_type) { diff --git a/esphome/components/modbus_controller/switch/modbus_switch.h b/esphome/components/modbus_controller/switch/modbus_switch.h index fe4b7c1ad5..0098076ef4 100644 --- a/esphome/components/modbus_controller/switch/modbus_switch.h +++ b/esphome/components/modbus_controller/switch/modbus_switch.h @@ -29,6 +29,7 @@ class ModbusSwitch : public Component, public switch_::Switch, public SensorItem void setup() override; void write_state(bool state) override; void dump_config() override; + void set_assumed_state(bool assumed_state); void set_state(bool state) { this->state = state; } void parse_and_publish(const std::vector &data) override; void set_parent(ModbusController *parent) { this->parent_ = parent; } @@ -40,10 +41,12 @@ class ModbusSwitch : public Component, public switch_::Switch, public SensorItem void set_use_write_mutiple(bool use_write_multiple) { this->use_write_multiple_ = use_write_multiple; } protected: + bool assumed_state() override; ModbusController *parent_{nullptr}; bool use_write_multiple_{false}; optional publish_transform_func_{nullopt}; optional write_transform_func_{nullopt}; + bool assumed_state_{false}; }; } // namespace modbus_controller diff --git a/esphome/components/mpl3115a2/mpl3115a2.cpp b/esphome/components/mpl3115a2/mpl3115a2.cpp index f1e553e107..9b65fb04e4 100644 --- a/esphome/components/mpl3115a2/mpl3115a2.cpp +++ b/esphome/components/mpl3115a2/mpl3115a2.cpp @@ -1,5 +1,6 @@ #include "mpl3115a2.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -8,7 +9,7 @@ namespace mpl3115a2 { static const char *const TAG = "mpl3115a2"; void MPL3115A2Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MPL3115A2..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t whoami = 0xFF; if (!this->read_byte(MPL3115A2_WHOAMI, &whoami, false)) { @@ -37,7 +38,7 @@ void MPL3115A2Component::dump_config() { if (this->is_failed()) { switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Communication with MPL3115A2 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case WRONG_ID: ESP_LOGE(TAG, "MPL3115A2 has invalid id"); diff --git a/esphome/components/mpr121/mpr121.cpp b/esphome/components/mpr121/mpr121.cpp index de364c59ff..39c45d7a89 100644 --- a/esphome/components/mpr121/mpr121.cpp +++ b/esphome/components/mpr121/mpr121.cpp @@ -11,7 +11,7 @@ namespace mpr121 { static const char *const TAG = "mpr121"; void MPR121Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MPR121..."); + ESP_LOGCONFIG(TAG, "Running setup"); // soft reset device this->write_byte(MPR121_SOFTRESET, 0x63); delay(100); // NOLINT @@ -72,7 +72,7 @@ void MPR121Component::dump_config() { LOG_I2C_DEVICE(this); switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Communication with MPR121 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case WRONG_CHIP_STATE: ESP_LOGE(TAG, "MPR121 has wrong default value for CONFIG2?"); diff --git a/esphome/components/mpu6050/mpu6050.cpp b/esphome/components/mpu6050/mpu6050.cpp index 64fcd3a2a8..84f0fb4bae 100644 --- a/esphome/components/mpu6050/mpu6050.cpp +++ b/esphome/components/mpu6050/mpu6050.cpp @@ -21,7 +21,7 @@ const uint8_t MPU6050_BIT_TEMPERATURE_DISABLED = 3; const float GRAVITY_EARTH = 9.80665f; void MPU6050Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MPU6050..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t who_am_i; if (!this->read_byte(MPU6050_REGISTER_WHO_AM_I, &who_am_i) || (who_am_i != 0x68 && who_am_i != 0x70 && who_am_i != 0x98)) { @@ -29,7 +29,7 @@ void MPU6050Component::setup() { return; } - ESP_LOGV(TAG, " Setting up Power Management..."); + ESP_LOGV(TAG, " Setting up Power Management"); // Setup power management uint8_t power_management; if (!this->read_byte(MPU6050_REGISTER_POWER_MANAGEMENT_1, &power_management)) { @@ -50,7 +50,7 @@ void MPU6050Component::setup() { return; } - ESP_LOGV(TAG, " Setting up Gyro Config..."); + ESP_LOGV(TAG, " Setting up Gyro Config"); // Set scale - 2000DPS uint8_t gyro_config; if (!this->read_byte(MPU6050_REGISTER_GYRO_CONFIG, &gyro_config)) { @@ -66,7 +66,7 @@ void MPU6050Component::setup() { return; } - ESP_LOGV(TAG, " Setting up Accel Config..."); + ESP_LOGV(TAG, " Setting up Accel Config"); // Set range - 2G uint8_t accel_config; if (!this->read_byte(MPU6050_REGISTER_ACCEL_CONFIG, &accel_config)) { @@ -86,7 +86,7 @@ void MPU6050Component::dump_config() { ESP_LOGCONFIG(TAG, "MPU6050:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with MPU6050 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Acceleration X", this->accel_x_sensor_); @@ -99,7 +99,7 @@ void MPU6050Component::dump_config() { } void MPU6050Component::update() { - ESP_LOGV(TAG, " Updating MPU6050..."); + ESP_LOGV(TAG, "Updating"); uint16_t raw_data[7]; if (!this->read_bytes_16(MPU6050_REGISTER_ACCEL_XOUT_H, raw_data, 7)) { this->status_set_warning(); diff --git a/esphome/components/mpu6886/mpu6886.cpp b/esphome/components/mpu6886/mpu6886.cpp index c296653e6b..cbd8b601bd 100644 --- a/esphome/components/mpu6886/mpu6886.cpp +++ b/esphome/components/mpu6886/mpu6886.cpp @@ -26,14 +26,14 @@ const float TEMPERATURE_SENSITIVITY = 326.8; const float TEMPERATURE_OFFSET = 25.0; void MPU6886Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MPU6886..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t who_am_i; if (!this->read_byte(MPU6886_REGISTER_WHO_AM_I, &who_am_i) || who_am_i != MPU6886_WHO_AM_I_IDENTIFIER) { this->mark_failed(); return; } - ESP_LOGV(TAG, " Setting up Power Management..."); + ESP_LOGV(TAG, " Setting up Power Management"); // Setup power management uint8_t power_management; if (!this->read_byte(MPU6886_REGISTER_POWER_MANAGEMENT_1, &power_management)) { @@ -54,7 +54,7 @@ void MPU6886Component::setup() { return; } - ESP_LOGV(TAG, " Setting up Gyroscope Config..."); + ESP_LOGV(TAG, " Setting up Gyroscope Config"); // Set scale - 2000DPS uint8_t gyro_config; if (!this->read_byte(MPU6886_REGISTER_GYRO_CONFIG, &gyro_config)) { @@ -70,7 +70,7 @@ void MPU6886Component::setup() { return; } - ESP_LOGV(TAG, " Setting up Accelerometer Config..."); + ESP_LOGV(TAG, " Setting up Accelerometer Config"); // Set range - 2G uint8_t accel_config; if (!this->read_byte(MPU6886_REGISTER_ACCEL_CONFIG, &accel_config)) { @@ -91,7 +91,7 @@ void MPU6886Component::dump_config() { ESP_LOGCONFIG(TAG, "MPU6886:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with MPU6886 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Acceleration X", this->accel_x_sensor_); @@ -104,7 +104,7 @@ void MPU6886Component::dump_config() { } void MPU6886Component::update() { - ESP_LOGV(TAG, " Updating MPU6886..."); + ESP_LOGV(TAG, " Updating"); uint16_t raw_data[7]; if (!this->read_bytes_16(MPU6886_REGISTER_ACCEL_XOUT_H, raw_data, 7)) { this->status_set_warning(); diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp index 4cc4773bd3..0a38598679 100644 --- a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp @@ -45,9 +45,13 @@ void MQTTAlarmControlPanelComponent::setup() { void MQTTAlarmControlPanelComponent::dump_config() { ESP_LOGCONFIG(TAG, "MQTT alarm_control_panel '%s':", this->alarm_control_panel_->get_name().c_str()); LOG_MQTT_COMPONENT(true, true) - ESP_LOGCONFIG(TAG, " Supported Features: %" PRIu32, this->alarm_control_panel_->get_supported_features()); - ESP_LOGCONFIG(TAG, " Requires Code to Disarm: %s", YESNO(this->alarm_control_panel_->get_requires_code())); - ESP_LOGCONFIG(TAG, " Requires Code To Arm: %s", YESNO(this->alarm_control_panel_->get_requires_code_to_arm())); + ESP_LOGCONFIG(TAG, + " Supported Features: %" PRIu32 "\n" + " Requires Code to Disarm: %s\n" + " Requires Code To Arm: %s", + this->alarm_control_panel_->get_supported_features(), + YESNO(this->alarm_control_panel_->get_requires_code()), + YESNO(this->alarm_control_panel_->get_requires_code_to_arm())); } void MQTTAlarmControlPanelComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { diff --git a/esphome/components/mqtt/mqtt_backend_esp32.cpp b/esphome/components/mqtt/mqtt_backend_esp32.cpp index 2cccb957eb..64dc27d84b 100644 --- a/esphome/components/mqtt/mqtt_backend_esp32.cpp +++ b/esphome/components/mqtt/mqtt_backend_esp32.cpp @@ -4,8 +4,8 @@ #ifdef USE_ESP32 #include -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace mqtt { diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index e3722099a7..ceb56bdfbe 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -34,7 +34,7 @@ MQTTClientComponent::MQTTClientComponent() { // Connection void MQTTClientComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up MQTT..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->mqtt_backend_.set_on_message( [this](const char *topic, const char *payload, size_t len, size_t index, size_t total) { if (index == 0) @@ -149,18 +149,23 @@ void MQTTClientComponent::send_device_info_() { } void MQTTClientComponent::dump_config() { - ESP_LOGCONFIG(TAG, "MQTT:"); - ESP_LOGCONFIG(TAG, " Server Address: %s:%u (%s)", this->credentials_.address.c_str(), this->credentials_.port, - this->ip_.str().c_str()); - ESP_LOGCONFIG(TAG, " Username: " LOG_SECRET("'%s'"), this->credentials_.username.c_str()); - ESP_LOGCONFIG(TAG, " Client ID: " LOG_SECRET("'%s'"), this->credentials_.client_id.c_str()); - ESP_LOGCONFIG(TAG, " Clean Session: %s", YESNO(this->credentials_.clean_session)); + ESP_LOGCONFIG(TAG, + "MQTT:\n" + " Server Address: %s:%u (%s)\n" + " Username: " LOG_SECRET("'%s'") "\n" + " Client ID: " LOG_SECRET("'%s'") "\n" + " Clean Session: %s", + this->credentials_.address.c_str(), this->credentials_.port, this->ip_.str().c_str(), + this->credentials_.username.c_str(), this->credentials_.client_id.c_str(), + YESNO(this->credentials_.clean_session)); if (this->is_discovery_ip_enabled()) { ESP_LOGCONFIG(TAG, " Discovery IP enabled"); } if (!this->discovery_info_.prefix.empty()) { - ESP_LOGCONFIG(TAG, " Discovery prefix: '%s'", this->discovery_info_.prefix.c_str()); - ESP_LOGCONFIG(TAG, " Discovery retain: %s", YESNO(this->discovery_info_.retain)); + ESP_LOGCONFIG(TAG, + " Discovery prefix: '%s'\n" + " Discovery retain: %s", + this->discovery_info_.prefix.c_str(), YESNO(this->discovery_info_.retain)); } ESP_LOGCONFIG(TAG, " Topic Prefix: '%s'", this->topic_prefix_.c_str()); if (!this->log_message_.topic.empty()) { @@ -201,13 +206,13 @@ void MQTTClientComponent::start_dnslookup_() { } case ERR_INPROGRESS: { // wait for callback - ESP_LOGD(TAG, "Resolving MQTT broker IP address..."); + ESP_LOGD(TAG, "Resolving broker IP address"); break; } default: case ERR_ARG: { // error - ESP_LOGW(TAG, "Error resolving MQTT broker IP address: %d", err); + ESP_LOGW(TAG, "Error resolving broker IP address: %d", err); break; } } @@ -221,7 +226,7 @@ void MQTTClientComponent::check_dnslookup_() { } if (this->dns_resolve_error_) { - ESP_LOGW(TAG, "Couldn't resolve IP address for '%s'!", this->credentials_.address.c_str()); + ESP_LOGW(TAG, "Couldn't resolve IP address for '%s'", this->credentials_.address.c_str()); this->state_ = MQTT_CLIENT_DISCONNECTED; return; } @@ -251,7 +256,7 @@ void MQTTClientComponent::start_connect_() { if (!network::is_connected()) return; - ESP_LOGI(TAG, "Connecting to MQTT..."); + ESP_LOGI(TAG, "Connecting"); // Force disconnect first this->mqtt_backend_.disconnect(); @@ -292,7 +297,7 @@ void MQTTClientComponent::check_connected() { this->state_ = MQTT_CLIENT_CONNECTED; this->sent_birth_message_ = false; this->status_clear_warning(); - ESP_LOGI(TAG, "MQTT Connected!"); + ESP_LOGI(TAG, "Connected"); // MQTT Client needs some time to be fully set up. delay(100); // NOLINT @@ -341,7 +346,7 @@ void MQTTClientComponent::loop() { if (!network::is_connected()) { reason_s = LOG_STR("WiFi disconnected"); } - ESP_LOGW(TAG, "MQTT Disconnected: %s.", LOG_STR_ARG(reason_s)); + ESP_LOGW(TAG, "Disconnected: %s", LOG_STR_ARG(reason_s)); this->disconnect_reason_.reset(); } @@ -364,7 +369,7 @@ void MQTTClientComponent::loop() { case MQTT_CLIENT_CONNECTED: if (!this->mqtt_backend_.connected()) { this->state_ = MQTT_CLIENT_DISCONNECTED; - ESP_LOGW(TAG, "Lost MQTT Client connection!"); + ESP_LOGW(TAG, "Lost client connection"); this->start_dnslookup_(); } else { if (!this->birth_message_.topic.empty() && !this->sent_birth_message_) { @@ -378,7 +383,7 @@ void MQTTClientComponent::loop() { } if (millis() - this->last_connected_ > this->reboot_timeout_ && this->reboot_timeout_ != 0) { - ESP_LOGE(TAG, "Can't connect to MQTT... Restarting..."); + ESP_LOGE(TAG, "Can't connect; restarting"); App.reboot(); } } @@ -396,7 +401,7 @@ bool MQTTClientComponent::subscribe_(const char *topic, uint8_t qos) { ESP_LOGV(TAG, "subscribe(topic='%s')", topic); } else { delay(5); - ESP_LOGV(TAG, "Subscribe failed for topic='%s'. Will retry later.", topic); + ESP_LOGV(TAG, "Subscribe failed for topic='%s'. Will retry", topic); this->status_momentary_warning("subscribe", 1000); } return ret != 0; @@ -499,7 +504,7 @@ bool MQTTClientComponent::publish(const MQTTMessage &message) { ESP_LOGV(TAG, "Publish(topic='%s' payload='%s' retain=%d qos=%d)", message.topic.c_str(), message.payload.c_str(), message.retain, message.qos); } else { - ESP_LOGV(TAG, "Publish failed for topic='%s' (len=%u). will retry later..", message.topic.c_str(), + ESP_LOGV(TAG, "Publish failed for topic='%s' (len=%u). Will retry", message.topic.c_str(), message.payload.length()); this->status_momentary_warning("publish", 1000); } @@ -515,7 +520,7 @@ bool MQTTClientComponent::publish_json(const std::string &topic, const json::jso void MQTTClientComponent::enable() { if (this->state_ != MQTT_CLIENT_DISABLED) return; - ESP_LOGD(TAG, "Enabling MQTT..."); + ESP_LOGD(TAG, "Enabling"); this->state_ = MQTT_CLIENT_DISCONNECTED; this->last_connected_ = millis(); this->start_dnslookup_(); @@ -524,7 +529,7 @@ void MQTTClientComponent::enable() { void MQTTClientComponent::disable() { if (this->state_ == MQTT_CLIENT_DISABLED) return; - ESP_LOGD(TAG, "Disabling MQTT..."); + ESP_LOGD(TAG, "Disabling"); this->state_ = MQTT_CLIENT_DISABLED; this->on_shutdown(); } @@ -721,9 +726,11 @@ void MQTTMessageTrigger::setup() { this->qos_); } void MQTTMessageTrigger::dump_config() { - ESP_LOGCONFIG(TAG, "MQTT Message Trigger:"); - ESP_LOGCONFIG(TAG, " Topic: '%s'", this->topic_.c_str()); - ESP_LOGCONFIG(TAG, " QoS: %u", this->qos_); + ESP_LOGCONFIG(TAG, + "MQTT Message Trigger:\n" + " Topic: '%s'\n" + " QoS: %u", + this->topic_.c_str(), this->qos_); } float MQTTMessageTrigger::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 3b9d367a7b..eee5644c9d 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -64,11 +64,11 @@ bool MQTTComponent::send_discovery_() { const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info(); if (discovery_info.clean) { - ESP_LOGV(TAG, "'%s': Cleaning discovery...", this->friendly_name().c_str()); + ESP_LOGV(TAG, "'%s': Cleaning discovery", this->friendly_name().c_str()); return global_mqtt_client->publish(this->get_discovery_topic_(discovery_info), "", 0, this->qos_, true); } - ESP_LOGV(TAG, "'%s': Sending discovery...", this->friendly_name().c_str()); + ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str()); return global_mqtt_client->publish_json( this->get_discovery_topic_(discovery_info), @@ -153,7 +153,7 @@ bool MQTTComponent::send_discovery_() { if (node_friendly_name.empty()) { node_friendly_name = node_name; } - const std::string &node_area = App.get_area(); + std::string node_area = App.get_area(); JsonObject device_info = root.createNestedObject(MQTT_DEVICE); const auto mac = get_mac_address(); diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index 0718a24828..8d09d836f3 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -54,12 +54,16 @@ void MQTTCoverComponent::dump_config() { bool has_command_topic = traits.get_supports_position() || !traits.get_supports_tilt(); LOG_MQTT_COMPONENT(true, has_command_topic) if (traits.get_supports_position()) { - ESP_LOGCONFIG(TAG, " Position State Topic: '%s'", this->get_position_state_topic().c_str()); - ESP_LOGCONFIG(TAG, " Position Command Topic: '%s'", this->get_position_command_topic().c_str()); + ESP_LOGCONFIG(TAG, + " Position State Topic: '%s'\n" + " Position Command Topic: '%s'", + this->get_position_state_topic().c_str(), this->get_position_command_topic().c_str()); } if (traits.get_supports_tilt()) { - ESP_LOGCONFIG(TAG, " Tilt State Topic: '%s'", this->get_tilt_state_topic().c_str()); - ESP_LOGCONFIG(TAG, " Tilt Command Topic: '%s'", this->get_tilt_command_topic().c_str()); + ESP_LOGCONFIG(TAG, + " Tilt State Topic: '%s'\n" + " Tilt Command Topic: '%s'", + this->get_tilt_state_topic().c_str(), this->get_tilt_command_topic().c_str()); } } void MQTTCoverComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index 9e5ea54bee..35713bdab6 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -121,16 +121,22 @@ void MQTTFanComponent::dump_config() { ESP_LOGCONFIG(TAG, "MQTT Fan '%s': ", this->state_->get_name().c_str()); LOG_MQTT_COMPONENT(true, true); if (this->state_->get_traits().supports_direction()) { - ESP_LOGCONFIG(TAG, " Direction State Topic: '%s'", this->get_direction_state_topic().c_str()); - ESP_LOGCONFIG(TAG, " Direction Command Topic: '%s'", this->get_direction_command_topic().c_str()); + ESP_LOGCONFIG(TAG, + " Direction State Topic: '%s'\n" + " Direction Command Topic: '%s'", + this->get_direction_state_topic().c_str(), this->get_direction_command_topic().c_str()); } if (this->state_->get_traits().supports_oscillation()) { - ESP_LOGCONFIG(TAG, " Oscillation State Topic: '%s'", this->get_oscillation_state_topic().c_str()); - ESP_LOGCONFIG(TAG, " Oscillation Command Topic: '%s'", this->get_oscillation_command_topic().c_str()); + ESP_LOGCONFIG(TAG, + " Oscillation State Topic: '%s'\n" + " Oscillation Command Topic: '%s'", + this->get_oscillation_state_topic().c_str(), this->get_oscillation_command_topic().c_str()); } if (this->state_->get_traits().supports_speed()) { - ESP_LOGCONFIG(TAG, " Speed Level State Topic: '%s'", this->get_speed_level_state_topic().c_str()); - ESP_LOGCONFIG(TAG, " Speed Level Command Topic: '%s'", this->get_speed_level_command_topic().c_str()); + ESP_LOGCONFIG(TAG, + " Speed Level State Topic: '%s'\n" + " Speed Level Command Topic: '%s'", + this->get_speed_level_state_topic().c_str(), this->get_speed_level_command_topic().c_str()); } } diff --git a/esphome/components/mqtt/mqtt_valve.cpp b/esphome/components/mqtt/mqtt_valve.cpp index 07eeca08d6..85e06fe79c 100644 --- a/esphome/components/mqtt/mqtt_valve.cpp +++ b/esphome/components/mqtt/mqtt_valve.cpp @@ -42,8 +42,10 @@ void MQTTValveComponent::dump_config() { bool has_command_topic = traits.get_supports_position(); LOG_MQTT_COMPONENT(true, has_command_topic) if (traits.get_supports_position()) { - ESP_LOGCONFIG(TAG, " Position State Topic: '%s'", this->get_position_state_topic().c_str()); - ESP_LOGCONFIG(TAG, " Position Command Topic: '%s'", this->get_position_command_topic().c_str()); + ESP_LOGCONFIG(TAG, + " Position State Topic: '%s'\n" + " Position Command Topic: '%s'", + this->get_position_state_topic().c_str(), this->get_position_command_topic().c_str()); } } void MQTTValveComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { diff --git a/esphome/components/ms5611/ms5611.cpp b/esphome/components/ms5611/ms5611.cpp index 4b34e1d71a..7a820f3b5a 100644 --- a/esphome/components/ms5611/ms5611.cpp +++ b/esphome/components/ms5611/ms5611.cpp @@ -15,7 +15,7 @@ static const uint8_t MS5611_CMD_CONV_D2 = 0x50; static const uint8_t MS5611_CMD_READ_PROM = 0xA2; void MS5611Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MS5611..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->write_bytes(MS5611_CMD_RESET, nullptr, 0)) { this->mark_failed(); return; @@ -32,7 +32,7 @@ void MS5611Component::dump_config() { ESP_LOGCONFIG(TAG, "MS5611:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with MS5611 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); diff --git a/esphome/components/ms8607/ms8607.cpp b/esphome/components/ms8607/ms8607.cpp index 4ad6ac336d..b985623b24 100644 --- a/esphome/components/ms8607/ms8607.cpp +++ b/esphome/components/ms8607/ms8607.cpp @@ -67,7 +67,7 @@ static uint8_t crc4(uint16_t *buffer, size_t length); static uint8_t hsensor_crc_check(uint16_t value); void MS8607Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MS8607..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->error_code_ = ErrorCode::NONE; this->setup_status_ = SetupStatus::NEEDS_RESET; @@ -140,7 +140,7 @@ void MS8607Component::dump_config() { // LOG_I2C_DEVICE doesn't work for humidity, the `address_` is protected. Log using get_address() ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->humidity_device_->get_address()); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with MS8607 failed."); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); switch (this->error_code_) { case ErrorCode::PT_RESET_FAILED: ESP_LOGE(TAG, "Temperature/Pressure RESET failed"); diff --git a/esphome/components/msa3xx/msa3xx.cpp b/esphome/components/msa3xx/msa3xx.cpp index 8ecb319955..17f0a9c418 100644 --- a/esphome/components/msa3xx/msa3xx.cpp +++ b/esphome/components/msa3xx/msa3xx.cpp @@ -1,7 +1,7 @@ #include "msa3xx.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace msa3xx { @@ -118,7 +118,7 @@ const char *orientation_xy_to_string(OrientationXY orientation) { const char *orientation_z_to_string(bool orientation) { return orientation ? "Downwards looking" : "Upwards looking"; } void MSA3xxComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up MSA3xx..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t part_id{0xff}; if (!this->read_byte(static_cast(RegisterMap::PART_ID), &part_id) || (part_id != MSA_3XX_PART_ID)) { @@ -159,15 +159,19 @@ void MSA3xxComponent::dump_config() { ESP_LOGCONFIG(TAG, "MSA3xx:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with MSA3xx failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } - ESP_LOGCONFIG(TAG, " Model: %s", model_to_string(this->model_)); - ESP_LOGCONFIG(TAG, " Power Mode: %s", power_mode_to_string(this->power_mode_)); - ESP_LOGCONFIG(TAG, " Bandwidth: %s", bandwidth_to_string(this->bandwidth_)); - ESP_LOGCONFIG(TAG, " Range: %s", range_to_string(this->range_)); - ESP_LOGCONFIG(TAG, " Resolution: %s", res_to_string(this->resolution_)); - ESP_LOGCONFIG(TAG, " Offsets: {%.3f m/s², %.3f m/s², %.3f m/s²}", this->offset_x_, this->offset_y_, this->offset_z_); - ESP_LOGCONFIG(TAG, " Transform: {mirror_x=%s, mirror_y=%s, mirror_z=%s, swap_xy=%s}", YESNO(this->swap_.x_polarity), + ESP_LOGCONFIG(TAG, + " Model: %s\n" + " Power Mode: %s\n" + " Bandwidth: %s\n" + " Range: %s\n" + " Resolution: %s\n" + " Offsets: {%.3f m/s², %.3f m/s², %.3f m/s²}\n" + " Transform: {mirror_x=%s, mirror_y=%s, mirror_z=%s, swap_xy=%s}", + model_to_string(this->model_), power_mode_to_string(this->power_mode_), + bandwidth_to_string(this->bandwidth_), range_to_string(this->range_), res_to_string(this->resolution_), + this->offset_x_, this->offset_y_, this->offset_z_, YESNO(this->swap_.x_polarity), YESNO(this->swap_.y_polarity), YESNO(this->swap_.z_polarity), YESNO(this->swap_.x_y_swap)); LOG_UPDATE_INTERVAL(this); @@ -248,10 +252,10 @@ void MSA3xxComponent::loop() { } void MSA3xxComponent::update() { - ESP_LOGV(TAG, "Updating MSA3xx..."); + ESP_LOGV(TAG, "Updating"); if (!this->is_ready()) { - ESP_LOGV(TAG, "Component MSA3xx not ready for update"); + ESP_LOGV(TAG, "Not ready for update"); return; } ESP_LOGV(TAG, "Acceleration: {x = %+1.3f m/s², y = %+1.3f m/s², z = %+1.3f m/s²}; ", this->data_.x, this->data_.y, diff --git a/esphome/components/my9231/my9231.cpp b/esphome/components/my9231/my9231.cpp index c511591856..691c945254 100644 --- a/esphome/components/my9231/my9231.cpp +++ b/esphome/components/my9231/my9231.cpp @@ -1,6 +1,6 @@ #include "my9231.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace my9231 { @@ -28,7 +28,7 @@ static const uint8_t MY9231_CMD_SCATTER_APDM = 0x0 << 0; static const uint8_t MY9231_CMD_SCATTER_PWM = 0x1 << 0; void MY9231OutputComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up MY9231OutputComponent..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->pin_di_->setup(); this->pin_di_->digital_write(false); this->pin_dcki_->setup(); @@ -63,9 +63,11 @@ void MY9231OutputComponent::dump_config() { ESP_LOGCONFIG(TAG, "MY9231:"); LOG_PIN(" DI Pin: ", this->pin_di_); LOG_PIN(" DCKI Pin: ", this->pin_dcki_); - ESP_LOGCONFIG(TAG, " Total number of channels: %u", this->num_channels_); - ESP_LOGCONFIG(TAG, " Number of chips: %u", this->num_chips_); - ESP_LOGCONFIG(TAG, " Bit depth: %u", this->bit_depth_); + ESP_LOGCONFIG(TAG, + " Total number of channels: %u\n" + " Number of chips: %u\n" + " Bit depth: %u", + this->num_channels_, this->num_chips_, this->bit_depth_); } void MY9231OutputComponent::loop() { if (!this->update_) diff --git a/esphome/components/nau7802/nau7802.cpp b/esphome/components/nau7802/nau7802.cpp index d7149e5697..edcd114852 100644 --- a/esphome/components/nau7802/nau7802.cpp +++ b/esphome/components/nau7802/nau7802.cpp @@ -52,8 +52,8 @@ static const uint8_t POWER_PGA_CAP_EN = 0x80; static const uint8_t DEVICE_REV = 0x1F; void NAU7802Sensor::setup() { + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); i2c::I2CRegister pu_ctrl = this->reg(PU_CTRL_REG); - ESP_LOGCONFIG(TAG, "Setting up NAU7802 '%s'...", this->name_.c_str()); uint8_t rev; if (this->read_register(DEVICE_REV | READ_BIT, &rev, 1)) { @@ -127,12 +127,14 @@ void NAU7802Sensor::dump_config() { LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with NAU7802 failed earlier, during setup"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL_FOR, this->get_name().c_str()); return; } // Note these may differ from the values on the device if calbration has been run - ESP_LOGCONFIG(TAG, " Offset Calibration: %s", to_string(this->offset_calibration_).c_str()); - ESP_LOGCONFIG(TAG, " Gain Calibration: %f", this->gain_calibration_); + ESP_LOGCONFIG(TAG, + " Offset Calibration: %s\n" + " Gain Calibration: %f", + to_string(this->offset_calibration_).c_str(), this->gain_calibration_); std::string voltage = "unknown"; switch (this->ldo_) { diff --git a/esphome/components/neopixelbus/neopixelbus_light.h b/esphome/components/neopixelbus/neopixelbus_light.h index 5233886075..d94a923614 100644 --- a/esphome/components/neopixelbus/neopixelbus_light.h +++ b/esphome/components/neopixelbus/neopixelbus_light.h @@ -2,10 +2,10 @@ #ifdef USE_ARDUINO -#include "esphome/core/macros.h" +#include "esphome/core/color.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" -#include "esphome/core/color.h" +#include "esphome/core/macros.h" #include "esphome/components/light/light_output.h" #include "esphome/components/light/addressable_light.h" diff --git a/esphome/components/network/ip_address.h b/esphome/components/network/ip_address.h index 1598daf6f9..d76da573b5 100644 --- a/esphome/components/network/ip_address.h +++ b/esphome/components/network/ip_address.h @@ -5,8 +5,8 @@ #include #include #include -#include "esphome/core/macros.h" #include "esphome/core/helpers.h" +#include "esphome/core/macros.h" #if defined(USE_ESP_IDF) || defined(USE_LIBRETINY) || USE_ARDUINO_VERSION_CODE > VERSION_CODE(3, 0, 0) #include diff --git a/esphome/components/network/util.cpp b/esphome/components/network/util.cpp index ed519f738a..a8e792a2d7 100644 --- a/esphome/components/network/util.cpp +++ b/esphome/components/network/util.cpp @@ -9,6 +9,10 @@ #include "esphome/components/ethernet/ethernet_component.h" #endif +#ifdef USE_OPENTHREAD +#include "esphome/components/openthread/openthread.h" +#endif + namespace esphome { namespace network { @@ -23,6 +27,11 @@ bool is_connected() { return wifi::global_wifi_component->is_connected(); #endif +#ifdef USE_OPENTHREAD + if (openthread::global_openthread_component != nullptr) + return openthread::global_openthread_component->is_connected(); +#endif + #ifdef USE_HOST return true; // Assume its connected #endif @@ -45,6 +54,10 @@ network::IPAddresses get_ip_addresses() { #ifdef USE_WIFI if (wifi::global_wifi_component != nullptr) return wifi::global_wifi_component->get_ip_addresses(); +#endif +#ifdef USE_OPENTHREAD + if (openthread::global_openthread_component != nullptr) + return openthread::global_openthread_component->get_ip_addresses(); #endif return {}; } diff --git a/esphome/components/nextion/base_component.py b/esphome/components/nextion/base_component.py index 0058d957dc..98dea4b513 100644 --- a/esphome/components/nextion/base_component.py +++ b/esphome/components/nextion/base_component.py @@ -14,6 +14,8 @@ CONF_COMPONENT_NAME = "component_name" CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start" CONF_FONT_ID = "font_id" CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color" +CONF_MAX_COMMANDS_PER_LOOP = "max_commands_per_loop" +CONF_MAX_QUEUE_SIZE = "max_queue_size" CONF_ON_BUFFER_OVERFLOW = "on_buffer_overflow" CONF_ON_PAGE = "on_page" CONF_ON_SETUP = "on_setup" diff --git a/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp b/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp index 499cd901c0..b6d4cc3f23 100644 --- a/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +++ b/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp @@ -16,7 +16,7 @@ void NextionBinarySensor::process_bool(const std::string &variable_name, bool st if (this->variable_name_ == variable_name) { this->publish_state(state); - ESP_LOGD(TAG, "Processed binarysensor \"%s\" state %s", variable_name.c_str(), state ? "ON" : "OFF"); + ESP_LOGD(TAG, "Binary sensor: %s=%s", variable_name.c_str(), ONOFF(state)); } } @@ -56,13 +56,12 @@ void NextionBinarySensor::set_state(bool state, bool publish, bool send_to_nexti this->publish_state(state); } else { this->state = state; - this->has_state_ = true; + this->set_has_state(true); } this->update_component_settings(); - ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %s", this->variable_name_.c_str(), - ONOFF(this->variable_name_.c_str())); + ESP_LOGN(TAG, "Write: %s=%s", this->variable_name_.c_str(), ONOFF(this->state)); } } // namespace nextion diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index 2e7c1c2825..7f63ca147b 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -16,10 +16,12 @@ from .base_component import ( CONF_AUTO_WAKE_ON_TOUCH, CONF_COMMAND_SPACING, CONF_EXIT_REPARSE_ON_START, + CONF_MAX_COMMANDS_PER_LOOP, + CONF_MAX_QUEUE_SIZE, CONF_ON_BUFFER_OVERFLOW, + CONF_ON_PAGE, CONF_ON_SETUP, CONF_ON_SLEEP, - CONF_ON_PAGE, CONF_ON_WAKE, CONF_SKIP_CONNECTION_HANDSHAKE, CONF_START_UP_PAGE, @@ -49,8 +51,27 @@ CONFIG_SCHEMA = ( display.BASIC_DISPLAY_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(Nextion), - cv.Optional(CONF_TFT_URL): cv.url, + cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean, cv.Optional(CONF_BRIGHTNESS): cv.percentage, + cv.Optional(CONF_COMMAND_SPACING): cv.All( + cv.positive_time_period_milliseconds, + cv.Range(max=TimePeriod(milliseconds=255)), + ), + cv.Optional(CONF_EXIT_REPARSE_ON_START, default=False): cv.boolean, + cv.Optional(CONF_MAX_COMMANDS_PER_LOOP): cv.uint16_t, + cv.Optional(CONF_MAX_QUEUE_SIZE): cv.positive_int, + cv.Optional(CONF_ON_BUFFER_OVERFLOW): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + BufferOverflowTrigger + ), + } + ), + cv.Optional(CONF_ON_PAGE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PageTrigger), + } + ), cv.Optional(CONF_ON_SETUP): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SetupTrigger), @@ -61,38 +82,21 @@ CONFIG_SCHEMA = ( cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SleepTrigger), } ), - cv.Optional(CONF_ON_WAKE): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(WakeTrigger), - } - ), - cv.Optional(CONF_ON_PAGE): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PageTrigger), - } - ), cv.Optional(CONF_ON_TOUCH): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TouchTrigger), } ), - cv.Optional(CONF_ON_BUFFER_OVERFLOW): automation.validate_automation( + cv.Optional(CONF_ON_WAKE): automation.validate_automation( { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - BufferOverflowTrigger - ), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(WakeTrigger), } ), + cv.Optional(CONF_SKIP_CONNECTION_HANDSHAKE, default=False): cv.boolean, + cv.Optional(CONF_START_UP_PAGE): cv.uint8_t, + cv.Optional(CONF_TFT_URL): cv.url, cv.Optional(CONF_TOUCH_SLEEP_TIMEOUT): cv.int_range(min=3, max=65535), cv.Optional(CONF_WAKE_UP_PAGE): cv.uint8_t, - cv.Optional(CONF_START_UP_PAGE): cv.uint8_t, - cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean, - cv.Optional(CONF_EXIT_REPARSE_ON_START, default=False): cv.boolean, - cv.Optional(CONF_SKIP_CONNECTION_HANDSHAKE, default=False): cv.boolean, - cv.Optional(CONF_COMMAND_SPACING): cv.All( - cv.positive_time_period_milliseconds, - cv.Range(max=TimePeriod(milliseconds=255)), - ), } ) .extend(cv.polling_component_schema("5s")) @@ -125,6 +129,10 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await uart.register_uart_device(var, config) + if max_queue_size := config.get(CONF_MAX_QUEUE_SIZE): + cg.add_define("USE_NEXTION_MAX_QUEUE_SIZE") + cg.add(var.set_max_queue_size(max_queue_size)) + if command_spacing := config.get(CONF_COMMAND_SPACING): cg.add_define("USE_NEXTION_COMMAND_SPACING") cg.add(var.set_command_spacing(command_spacing.total_milliseconds)) @@ -167,6 +175,10 @@ async def to_code(config): cg.add(var.set_skip_connection_handshake(config[CONF_SKIP_CONNECTION_HANDSHAKE])) + if max_commands_per_loop := config.get(CONF_MAX_COMMANDS_PER_LOOP): + cg.add_define("USE_NEXTION_MAX_COMMANDS_PER_LOOP") + cg.add(var.set_max_commands_per_loop(max_commands_per_loop)) + await display.register_display(var, config) for conf in config.get(CONF_ON_SETUP, []): diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index 38e37300af..3de32bfde9 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -37,7 +37,7 @@ bool Nextion::send_command_(const std::string &command) { } #endif // USE_NEXTION_COMMAND_SPACING - ESP_LOGN(TAG, "send_command %s", command.c_str()); + ESP_LOGN(TAG, "cmd: %s", command.c_str()); this->write_str(command.c_str()); const uint8_t to_send[3] = {0xFF, 0xFF, 0xFF}; @@ -57,7 +57,7 @@ bool Nextion::check_connect_() { // Check if the handshake should be skipped for the Nextion connection if (this->skip_connection_handshake_) { // Log the connection status without handshake - ESP_LOGW(TAG, "Nextion display set as connected without performing handshake"); + ESP_LOGW(TAG, "Connected (no handshake)"); // Set the connection status to true this->is_connected_ = true; // Return true indicating the connection is set @@ -88,27 +88,27 @@ bool Nextion::check_connect_() { this->recv_ret_string_(response, 0, false); if (!response.empty() && response[0] == 0x1A) { // Swallow invalid variable name responses that may be caused by the above commands - ESP_LOGD(TAG, "0x1A error ignored during setup"); + ESP_LOGD(TAG, "0x1A error ignored (setup)"); return false; } if (response.empty() || response.find("comok") == std::string::npos) { #ifdef NEXTION_PROTOCOL_LOG - ESP_LOGN(TAG, "Bad connect request %s", response.c_str()); + ESP_LOGN(TAG, "Bad connect: %s", response.c_str()); for (size_t i = 0; i < response.length(); i++) { - ESP_LOGN(TAG, "response %s %d %d %c", response.c_str(), i, response[i], response[i]); + ESP_LOGN(TAG, "resp: %s %d %d %c", response.c_str(), i, response[i], response[i]); } #endif - ESP_LOGW(TAG, "Nextion is not connected! "); + ESP_LOGW(TAG, "Not connected"); comok_sent_ = 0; return false; } this->ignore_is_setup_ = true; - ESP_LOGI(TAG, "Nextion is connected"); + ESP_LOGI(TAG, "Connected"); this->is_connected_ = true; - ESP_LOGN(TAG, "connect request %s", response.c_str()); + ESP_LOGN(TAG, "connect: %s", response.c_str()); size_t start; size_t end = 0; @@ -120,14 +120,14 @@ bool Nextion::check_connect_() { this->is_detected_ = (connect_info.size() == 7); if (this->is_detected_) { - ESP_LOGN(TAG, "Received connect_info %zu", connect_info.size()); + ESP_LOGN(TAG, "Connect info: %zu", connect_info.size()); this->device_model_ = connect_info[2]; this->firmware_version_ = connect_info[3]; this->serial_number_ = connect_info[5]; this->flash_size_ = connect_info[6]; } else { - ESP_LOGE(TAG, "Nextion returned bad connect value \"%s\"", response.c_str()); + ESP_LOGE(TAG, "Bad connect value: '%s'", response.c_str()); } this->ignore_is_setup_ = false; @@ -148,31 +148,43 @@ void Nextion::reset_(bool reset_nextion) { void Nextion::dump_config() { ESP_LOGCONFIG(TAG, "Nextion:"); if (this->skip_connection_handshake_) { - ESP_LOGCONFIG(TAG, " Skip handshake: %s", YESNO(this->skip_connection_handshake_)); + ESP_LOGCONFIG(TAG, " Skip handshake: %s", YESNO(this->skip_connection_handshake_)); } else { - ESP_LOGCONFIG(TAG, " Device Model: %s", this->device_model_.c_str()); - ESP_LOGCONFIG(TAG, " Firmware Version: %s", this->firmware_version_.c_str()); - ESP_LOGCONFIG(TAG, " Serial Number: %s", this->serial_number_.c_str()); - ESP_LOGCONFIG(TAG, " Flash Size: %s", this->flash_size_.c_str()); + ESP_LOGCONFIG(TAG, + " Device Model: %s\n" + " FW Version: %s\n" + " Serial Number: %s\n" + " Flash Size: %s", + this->device_model_.c_str(), this->firmware_version_.c_str(), this->serial_number_.c_str(), + this->flash_size_.c_str()); } - ESP_LOGCONFIG(TAG, " Wake On Touch: %s", YESNO(this->auto_wake_on_touch_)); - ESP_LOGCONFIG(TAG, " Exit reparse: %s", YESNO(this->exit_reparse_on_start_)); + ESP_LOGCONFIG(TAG, + " Wake On Touch: %s\n" + " Exit reparse: %s", + YESNO(this->auto_wake_on_touch_), YESNO(this->exit_reparse_on_start_)); +#ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP + ESP_LOGCONFIG(TAG, " Max commands per loop: %u", this->max_commands_per_loop_); +#endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP if (this->touch_sleep_timeout_ != 0) { - ESP_LOGCONFIG(TAG, " Touch Timeout: %" PRIu32, this->touch_sleep_timeout_); + ESP_LOGCONFIG(TAG, " Touch Timeout: %" PRIu32, this->touch_sleep_timeout_); } if (this->wake_up_page_ != -1) { - ESP_LOGCONFIG(TAG, " Wake Up Page: %" PRId16, this->wake_up_page_); + ESP_LOGCONFIG(TAG, " Wake Up Page: %d", this->wake_up_page_); } if (this->start_up_page_ != -1) { - ESP_LOGCONFIG(TAG, " Start Up Page: %" PRId16, this->start_up_page_); + ESP_LOGCONFIG(TAG, " Start Up Page: %d", this->start_up_page_); } #ifdef USE_NEXTION_COMMAND_SPACING - ESP_LOGCONFIG(TAG, " Command spacing: %" PRIu8 "ms", this->command_pacer_.get_spacing()); + ESP_LOGCONFIG(TAG, " Cmd spacing: %u ms", this->command_pacer_.get_spacing()); #endif // USE_NEXTION_COMMAND_SPACING + +#ifdef USE_NEXTION_MAX_QUEUE_SIZE + ESP_LOGCONFIG(TAG, " Max queue size: %zu", this->max_queue_size_); +#endif } float Nextion::get_setup_priority() const { return setup_priority::DATA; } @@ -248,7 +260,7 @@ bool Nextion::send_command_printf(const char *format, ...) { int ret = vsnprintf(buffer, sizeof(buffer), format, arg); va_end(arg); if (ret <= 0) { - ESP_LOGW(TAG, "Building command for format '%s' failed!", format); + ESP_LOGW(TAG, "Bad cmd format: '%s'", format); return false; } @@ -269,9 +281,9 @@ void Nextion::print_queue_members_() { break; if (i == nullptr) { - ESP_LOGN(TAG, "Nextion queue is null"); + ESP_LOGN(TAG, "Queue null"); } else { - ESP_LOGN(TAG, "Nextion queue type: %d:%s , name: %s", i->component->get_queue_type(), + ESP_LOGN(TAG, "Queue type: %d:%s, name: %s", i->component->get_queue_type(), i->component->get_queue_type_string().c_str(), i->component->get_variable_name().c_str()); } } @@ -312,7 +324,7 @@ void Nextion::loop() { this->started_ms_ = millis(); if (this->started_ms_ + this->startup_override_ms_ < millis()) { - ESP_LOGD(TAG, "Manually set nextion report ready"); + ESP_LOGD(TAG, "Manual ready set"); this->nextion_reports_is_setup_ = true; } } @@ -321,20 +333,20 @@ void Nextion::loop() { bool Nextion::remove_from_q_(bool report_empty) { if (this->nextion_queue_.empty()) { if (report_empty) { - ESP_LOGE(TAG, "Nextion queue is empty!"); + ESP_LOGE(TAG, "Queue empty"); } return false; } NextionQueue *nb = this->nextion_queue_.front(); if (!nb || !nb->component) { - ESP_LOGE(TAG, "Invalid queue entry!"); + ESP_LOGE(TAG, "Invalid queue"); this->nextion_queue_.pop_front(); return false; } NextionComponentBase *component = nb->component; - ESP_LOGN(TAG, "Removing %s from the queue", component->get_variable_name().c_str()); + ESP_LOGN(TAG, "Removed: %s", component->get_variable_name().c_str()); if (component->get_queue_type() == NextionQueueType::NO_RESULT) { if (component->get_variable_name() == "sleep_wake") { @@ -361,6 +373,10 @@ void Nextion::process_nextion_commands_() { return; } +#ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP + size_t commands_processed = 0; +#endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP + #ifdef USE_NEXTION_COMMAND_SPACING if (!this->command_pacer_.can_send()) { return; // Will try again in next loop iteration @@ -370,16 +386,22 @@ void Nextion::process_nextion_commands_() { size_t to_process_length = 0; std::string to_process; - ESP_LOGN(TAG, "this->command_data_ %s length %d", this->command_data_.c_str(), this->command_data_.length()); + ESP_LOGN(TAG, "command_data_ %s len %d", this->command_data_.c_str(), this->command_data_.length()); #ifdef NEXTION_PROTOCOL_LOG this->print_queue_members_(); #endif while ((to_process_length = this->command_data_.find(COMMAND_DELIMITER)) != std::string::npos) { - ESP_LOGN(TAG, "print_queue_members_ size %zu", this->nextion_queue_.size()); +#ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP + if (++commands_processed > this->max_commands_per_loop_) { + ESP_LOGW(TAG, "Command processing limit exceeded"); + break; + } +#endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP + ESP_LOGN(TAG, "queue size: %zu", this->nextion_queue_.size()); while (to_process_length + COMMAND_DELIMITER.length() < this->command_data_.length() && static_cast(this->command_data_[to_process_length + COMMAND_DELIMITER.length()]) == 0xFF) { ++to_process_length; - ESP_LOGN(TAG, "Add extra 0xFF to process"); + ESP_LOGN(TAG, "Add 0xFF"); } this->nextion_event_ = this->command_data_[0]; @@ -389,19 +411,19 @@ void Nextion::process_nextion_commands_() { switch (this->nextion_event_) { case 0x00: // instruction sent by user has failed - ESP_LOGW(TAG, "Nextion reported invalid instruction!"); + ESP_LOGW(TAG, "Invalid instruction"); this->remove_from_q_(); break; case 0x01: // instruction sent by user was successful - ESP_LOGVV(TAG, "instruction sent by user was successful"); + ESP_LOGVV(TAG, "Cmd OK"); ESP_LOGN(TAG, "this->nextion_queue_.empty() %s", this->nextion_queue_.empty() ? "True" : "False"); this->remove_from_q_(); if (!this->is_setup_) { if (this->nextion_queue_.empty()) { - ESP_LOGD(TAG, "Nextion is setup"); + ESP_LOGD(TAG, "Setup complete"); this->is_setup_ = true; this->setup_callback_.call(); } @@ -411,96 +433,91 @@ void Nextion::process_nextion_commands_() { #endif break; case 0x02: // invalid Component ID or name was used - ESP_LOGW(TAG, "Nextion reported component ID or name invalid!"); + ESP_LOGW(TAG, "Invalid component ID/name"); this->remove_from_q_(); break; case 0x03: // invalid Page ID or name was used - ESP_LOGW(TAG, "Nextion reported page ID invalid!"); + ESP_LOGW(TAG, "Invalid page ID"); this->remove_from_q_(); break; case 0x04: // invalid Picture ID was used - ESP_LOGW(TAG, "Nextion reported picture ID invalid!"); + ESP_LOGW(TAG, "Invalid picture ID"); this->remove_from_q_(); break; case 0x05: // invalid Font ID was used - ESP_LOGW(TAG, "Nextion reported font ID invalid!"); + ESP_LOGW(TAG, "Invalid font ID"); this->remove_from_q_(); break; case 0x06: // File operation fails - ESP_LOGW(TAG, "Nextion File operation fail!"); + ESP_LOGW(TAG, "File operation failed"); break; case 0x09: // Instructions with CRC validation fails their CRC check - ESP_LOGW(TAG, "Nextion Instructions with CRC validation fails their CRC check!"); + ESP_LOGW(TAG, "CRC validation failed"); break; case 0x11: // invalid Baud rate was used - ESP_LOGW(TAG, "Nextion reported baud rate invalid!"); + ESP_LOGW(TAG, "Invalid baud rate"); break; case 0x12: // invalid Waveform ID or Channel # was used if (this->waveform_queue_.empty()) { - ESP_LOGW(TAG, - "Nextion reported invalid Waveform ID or Channel # was used but no waveform sensor in queue found!"); + ESP_LOGW(TAG, "Waveform ID/ch used but no sensor queued"); } else { auto &nb = this->waveform_queue_.front(); NextionComponentBase *component = nb->component; - ESP_LOGW(TAG, "Nextion reported invalid Waveform ID %d or Channel # %d was used!", - component->get_component_id(), component->get_wave_channel_id()); + ESP_LOGW(TAG, "Invalid waveform ID %d/ch %d", component->get_component_id(), + component->get_wave_channel_id()); - ESP_LOGN(TAG, "Removing waveform from queue with component id %d and waveform id %d", - component->get_component_id(), component->get_wave_channel_id()); + ESP_LOGN(TAG, "Remove waveform ID %d/ch %d", component->get_component_id(), component->get_wave_channel_id()); delete nb; // NOLINT(cppcoreguidelines-owning-memory) this->waveform_queue_.pop_front(); } break; case 0x1A: // variable name invalid - ESP_LOGW(TAG, "Nextion reported variable name invalid!"); + ESP_LOGW(TAG, "Invalid variable name"); this->remove_from_q_(); break; case 0x1B: // variable operation invalid - ESP_LOGW(TAG, "Nextion reported variable operation invalid!"); + ESP_LOGW(TAG, "Invalid variable operation"); this->remove_from_q_(); break; case 0x1C: // failed to assign - ESP_LOGW(TAG, "Nextion reported failed to assign variable!"); + ESP_LOGW(TAG, "Variable assign failed"); this->remove_from_q_(); break; case 0x1D: // operate EEPROM failed - ESP_LOGW(TAG, "Nextion reported operating EEPROM failed!"); + ESP_LOGW(TAG, "EEPROM operation failed"); break; case 0x1E: // parameter quantity invalid - ESP_LOGW(TAG, "Nextion reported parameter quantity invalid!"); + ESP_LOGW(TAG, "Invalid parameter count"); this->remove_from_q_(); break; case 0x1F: // IO operation failed - ESP_LOGW(TAG, "Nextion reported component I/O operation invalid!"); + ESP_LOGW(TAG, "Invalid component I/O"); break; case 0x20: // undefined escape characters - ESP_LOGW(TAG, "Nextion reported undefined escape characters!"); + ESP_LOGW(TAG, "Undefined escape chars"); this->remove_from_q_(); break; case 0x23: // too long variable name - ESP_LOGW(TAG, "Nextion reported too long variable name!"); + ESP_LOGW(TAG, "Variable name too long"); this->remove_from_q_(); break; case 0x24: // Serial Buffer overflow occurs // Buffer will continue to receive the current instruction, all previous instructions are lost. - ESP_LOGE(TAG, "Nextion reported Serial Buffer overflow!"); + ESP_LOGE(TAG, "Serial buffer overflow"); this->buffer_overflow_callback_.call(); break; case 0x65: { // touch event return data if (to_process_length != 3) { - ESP_LOGW(TAG, "Touch event data is expecting 3, received %zu", to_process_length); + ESP_LOGW(TAG, "Incorrect touch len: %zu (need 3)", to_process_length); break; } uint8_t page_id = to_process[0]; uint8_t component_id = to_process[1]; uint8_t touch_event = to_process[2]; // 0 -> release, 1 -> press - ESP_LOGD(TAG, "Got touch event:"); - ESP_LOGD(TAG, " page_id: %u", page_id); - ESP_LOGD(TAG, " component_id: %u", component_id); - ESP_LOGD(TAG, " event type: %s", touch_event ? "PRESS" : "RELEASE"); + ESP_LOGD(TAG, "Touch %s: page %u comp %u", touch_event ? "PRESS" : "RELEASE", page_id, component_id); for (auto *touch : this->touch_) { touch->process_touch(page_id, component_id, touch_event != 0); } @@ -510,12 +527,12 @@ void Nextion::process_nextion_commands_() { case 0x66: { // Nextion initiated new page event return data. // Also is used for sendme command which we never explicitly initiate if (to_process_length != 1) { - ESP_LOGW(TAG, "New page event data is expecting 1, received %zu", to_process_length); + ESP_LOGW(TAG, "Page event: expect 1, got %zu", to_process_length); break; } uint8_t page_id = to_process[0]; - ESP_LOGD(TAG, "Got new page: %u", page_id); + ESP_LOGD(TAG, "New page: %u", page_id); this->page_callback_.call(page_id); break; } @@ -525,7 +542,7 @@ void Nextion::process_nextion_commands_() { case 0x68: { // touch coordinate data (sleep) if (to_process_length != 5) { - ESP_LOGW(TAG, "Touch coordinate data is expecting 5, received %zu", to_process_length); + ESP_LOGW(TAG, "Touch coordinate: expect 5, got %zu", to_process_length); ESP_LOGW(TAG, "%s", to_process.c_str()); break; } @@ -533,10 +550,7 @@ void Nextion::process_nextion_commands_() { uint16_t x = (uint16_t(to_process[0]) << 8) | to_process[1]; uint16_t y = (uint16_t(to_process[2]) << 8) | to_process[3]; uint8_t touch_event = to_process[4]; // 0 -> release, 1 -> press - ESP_LOGD(TAG, "Got touch event:"); - ESP_LOGD(TAG, " x: %u", x); - ESP_LOGD(TAG, " y: %u", y); - ESP_LOGD(TAG, " type: %s", touch_event ? "PRESS" : "RELEASE"); + ESP_LOGD(TAG, "Touch %s at %u,%u", touch_event ? "PRESS" : "RELEASE", x, y); break; } @@ -547,25 +561,23 @@ void Nextion::process_nextion_commands_() { case 0x70: // string variable data return { if (this->nextion_queue_.empty()) { - ESP_LOGW(TAG, "ERROR: Received string return but the queue is empty"); + ESP_LOGW(TAG, "String return but queue is empty"); break; } NextionQueue *nb = this->nextion_queue_.front(); if (!nb || !nb->component) { - ESP_LOGE(TAG, "Invalid queue entry!"); + ESP_LOGE(TAG, "Invalid queue entry"); this->nextion_queue_.pop_front(); return; } NextionComponentBase *component = nb->component; if (component->get_queue_type() != NextionQueueType::TEXT_SENSOR) { - ESP_LOGE(TAG, "ERROR: Received string return but next in queue \"%s\" is not a text sensor", - component->get_variable_name().c_str()); + ESP_LOGE(TAG, "String return but '%s' not text sensor", component->get_variable_name().c_str()); } else { - ESP_LOGN(TAG, "Received get_string response: \"%s\" for component id: %s, type: %s", to_process.c_str(), - component->get_variable_name().c_str(), component->get_queue_type_string().c_str()); - component->set_state_from_string(to_process, true, false); + ESP_LOGN(TAG, "String resp: '%s' id: %s type: %s", to_process.c_str(), component->get_variable_name().c_str(), + component->get_queue_type_string().c_str()); } delete nb; // NOLINT(cppcoreguidelines-owning-memory) @@ -581,12 +593,12 @@ void Nextion::process_nextion_commands_() { case 0x71: // numeric variable data return { if (this->nextion_queue_.empty()) { - ESP_LOGE(TAG, "ERROR: Received numeric return but the queue is empty"); + ESP_LOGE(TAG, "Numeric return but queue empty"); break; } if (to_process_length == 0) { - ESP_LOGE(TAG, "ERROR: Received numeric return but no data!"); + ESP_LOGE(TAG, "Numeric return but no data"); break; } @@ -598,7 +610,7 @@ void Nextion::process_nextion_commands_() { NextionQueue *nb = this->nextion_queue_.front(); if (!nb || !nb->component) { - ESP_LOGE(TAG, "Invalid queue entry!"); + ESP_LOGE(TAG, "Invalid queue"); this->nextion_queue_.pop_front(); return; } @@ -607,12 +619,11 @@ void Nextion::process_nextion_commands_() { if (component->get_queue_type() != NextionQueueType::SENSOR && component->get_queue_type() != NextionQueueType::BINARY_SENSOR && component->get_queue_type() != NextionQueueType::SWITCH) { - ESP_LOGE(TAG, "ERROR: Received numeric return but next in queue \"%s\" is not a valid sensor type %d", - component->get_variable_name().c_str(), component->get_queue_type()); + ESP_LOGE(TAG, "Numeric return but '%s' invalid type %d", component->get_variable_name().c_str(), + component->get_queue_type()); } else { - ESP_LOGN(TAG, "Received numeric return for variable %s, queue type %d:%s, value %d", - component->get_variable_name().c_str(), component->get_queue_type(), - component->get_queue_type_string().c_str(), value); + ESP_LOGN(TAG, "Numeric: %s type %d:%s val %d", component->get_variable_name().c_str(), + component->get_queue_type(), component->get_queue_type_string().c_str(), value); component->set_state_from_int(value, true, false); } @@ -623,14 +634,14 @@ void Nextion::process_nextion_commands_() { } case 0x86: { // device automatically enters into sleep mode - ESP_LOGVV(TAG, "Received Nextion entering sleep automatically"); + ESP_LOGVV(TAG, "Auto sleep"); this->is_sleeping_ = true; this->sleep_callback_.call(); break; } case 0x87: // device automatically wakes up { - ESP_LOGVV(TAG, "Received Nextion leaves sleep automatically"); + ESP_LOGVV(TAG, "Auto wake"); this->is_sleeping_ = false; this->wake_callback_.call(); this->all_components_send_state_(false); @@ -638,7 +649,7 @@ void Nextion::process_nextion_commands_() { } case 0x88: // system successful start up { - ESP_LOGD(TAG, "system successful start up %zu", to_process_length); + ESP_LOGD(TAG, "System start: %zu", to_process_length); this->nextion_reports_is_setup_ = true; break; } @@ -657,17 +668,15 @@ void Nextion::process_nextion_commands_() { // Get variable name auto index = to_process.find('\0'); if (index == std::string::npos || (to_process_length - index - 1) < 1) { - ESP_LOGE(TAG, "Bad switch component data received for 0x90 event!"); - ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index); + ESP_LOGE(TAG, "Bad switch data (0x90)"); + ESP_LOGN(TAG, "proc: %s %zu %d", to_process.c_str(), to_process_length, index); break; } variable_name = to_process.substr(0, index); ++index; - ESP_LOGN(TAG, "Got Switch:"); - ESP_LOGN(TAG, " variable_name: %s", variable_name.c_str()); - ESP_LOGN(TAG, " value: %d", to_process[0] != 0); + ESP_LOGN(TAG, "Switch %s: %s", ONOFF(to_process[index] != 0), variable_name.c_str()); for (auto *switchtype : this->switchtype_) { switchtype->process_bool(variable_name, to_process[index] != 0); @@ -685,8 +694,8 @@ void Nextion::process_nextion_commands_() { auto index = to_process.find('\0'); if (index == std::string::npos || (to_process_length - index - 1) != 4) { - ESP_LOGE(TAG, "Bad sensor component data received for 0x91 event!"); - ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index); + ESP_LOGE(TAG, "Bad sensor data (0x91)"); + ESP_LOGN(TAG, "proc: %s %zu %d", to_process.c_str(), to_process_length, index); break; } @@ -698,9 +707,7 @@ void Nextion::process_nextion_commands_() { value += to_process[i + index + 1] << (8 * i); } - ESP_LOGN(TAG, "Got sensor:"); - ESP_LOGN(TAG, " variable_name: %s", variable_name.c_str()); - ESP_LOGN(TAG, " value: %d", value); + ESP_LOGN(TAG, "Sensor: %s=%d", variable_name.c_str(), value); for (auto *sensor : this->sensortype_) { sensor->process_sensor(variable_name, value); @@ -722,8 +729,8 @@ void Nextion::process_nextion_commands_() { // Get variable name auto index = to_process.find('\0'); if (index == std::string::npos || (to_process_length - index - 1) < 1) { - ESP_LOGE(TAG, "Bad text sensor component data received for 0x92 event!"); - ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index); + ESP_LOGE(TAG, "Bad text data (0x92)"); + ESP_LOGN(TAG, "proc: %s %zu %d", to_process.c_str(), to_process_length, index); break; } @@ -732,9 +739,7 @@ void Nextion::process_nextion_commands_() { text_value = to_process.substr(index); - ESP_LOGN(TAG, "Got Text Sensor:"); - ESP_LOGN(TAG, " variable_name: %s", variable_name.c_str()); - ESP_LOGN(TAG, " value: %s", text_value.c_str()); + ESP_LOGN(TAG, "Text sensor: %s='%s'", variable_name.c_str(), text_value.c_str()); // NextionTextSensorResponseQueue *nq = new NextionTextSensorResponseQueue; // nq->variable_name = variable_name; @@ -757,17 +762,15 @@ void Nextion::process_nextion_commands_() { // Get variable name auto index = to_process.find('\0'); if (index == std::string::npos || (to_process_length - index - 1) < 1) { - ESP_LOGE(TAG, "Bad binary sensor component data received for 0x92 event!"); - ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index); + ESP_LOGE(TAG, "Bad binary data (0x92)"); + ESP_LOGN(TAG, "proc: %s %zu %d", to_process.c_str(), to_process_length, index); break; } variable_name = to_process.substr(0, index); ++index; - ESP_LOGN(TAG, "Got Binary Sensor:"); - ESP_LOGN(TAG, " variable_name: %s", variable_name.c_str()); - ESP_LOGN(TAG, " value: %d", to_process[index] != 0); + ESP_LOGN(TAG, "Binary sensor: %s=%s", variable_name.c_str(), ONOFF(to_process[index] != 0)); for (auto *binarysensortype : this->binarysensortype_) { binarysensortype->process_bool(&variable_name[0], to_process[index] != 0); @@ -775,14 +778,14 @@ void Nextion::process_nextion_commands_() { break; } case 0xFD: { // data transparent transmit finished - ESP_LOGVV(TAG, "Nextion reported data transmit finished!"); + ESP_LOGVV(TAG, "Data transmit done"); this->check_pending_waveform_(); break; } case 0xFE: { // data transparent transmit ready - ESP_LOGVV(TAG, "Nextion reported ready for transmit!"); + ESP_LOGVV(TAG, "Ready for transmit"); if (this->waveform_queue_.empty()) { - ESP_LOGE(TAG, "No waveforms in queue to send data!"); + ESP_LOGE(TAG, "No waveforms queued"); break; } @@ -793,8 +796,8 @@ void Nextion::process_nextion_commands_() { this->write_array(component->get_wave_buffer().data(), static_cast(buffer_to_send)); - ESP_LOGN(TAG, "Nextion sending waveform data for component id %d and waveform id %d, size %zu", - component->get_component_id(), component->get_wave_channel_id(), buffer_to_send); + ESP_LOGN(TAG, "Send waveform: component id %d, waveform id %d, size %zu", component->get_component_id(), + component->get_wave_channel_id(), buffer_to_send); component->clear_wave_buffer(buffer_to_send); delete nb; // NOLINT(cppcoreguidelines-owning-memory) @@ -802,14 +805,12 @@ void Nextion::process_nextion_commands_() { break; } default: - ESP_LOGW(TAG, "Received unknown event from nextion: 0x%02X", this->nextion_event_); + ESP_LOGW(TAG, "Unknown event: 0x%02X", this->nextion_event_); break; } // ESP_LOGN(TAG, "nextion_event_ deleting from 0 to %d", to_process_length + COMMAND_DELIMITER.length() + 1); this->command_data_.erase(0, to_process_length + COMMAND_DELIMITER.length() + 1); - // App.feed_wdt(); Remove before master merge - this->process_serial_(); } uint32_t ms = millis(); @@ -819,15 +820,15 @@ void Nextion::process_nextion_commands_() { NextionComponentBase *component = this->nextion_queue_[i]->component; if (this->nextion_queue_[i]->queue_time + this->max_q_age_ms_ < ms) { if (this->nextion_queue_[i]->queue_time == 0) { - ESP_LOGD(TAG, "Removing old queue type \"%s\" name \"%s\" queue_time 0", - component->get_queue_type_string().c_str(), component->get_variable_name().c_str()); + ESP_LOGD(TAG, "Remove old queue '%s':'%s' (t=0)", component->get_queue_type_string().c_str(), + component->get_variable_name().c_str()); } if (component->get_variable_name() == "sleep_wake") { this->is_sleeping_ = false; } - ESP_LOGD(TAG, "Removing old queue type \"%s\" name \"%s\"", component->get_queue_type_string().c_str(), + ESP_LOGD(TAG, "Remove old queue '%s':'%s'", component->get_queue_type_string().c_str(), component->get_variable_name().c_str()); if (component->get_queue_type() == NextionQueueType::NO_RESULT) { @@ -847,20 +848,17 @@ void Nextion::process_nextion_commands_() { } } } - ESP_LOGN(TAG, "Loop End"); + ESP_LOGN(TAG, "Loop end"); // App.feed_wdt(); Remove before master merge this->process_serial_(); -} // namespace nextion +} // Nextion::process_nextion_commands_() void Nextion::set_nextion_sensor_state(int queue_type, const std::string &name, float state) { this->set_nextion_sensor_state(static_cast(queue_type), name, state); } void Nextion::set_nextion_sensor_state(NextionQueueType queue_type, const std::string &name, float state) { - ESP_LOGN(TAG, "Received state:"); - ESP_LOGN(TAG, " variable: %s", name.c_str()); - ESP_LOGN(TAG, " state: %lf", state); - ESP_LOGN(TAG, " queue type: %d", queue_type); + ESP_LOGN(TAG, "State: %s=%lf (type %d)", name.c_str(), state, queue_type); switch (queue_type) { case NextionQueueType::SENSOR: { @@ -891,15 +889,13 @@ void Nextion::set_nextion_sensor_state(NextionQueueType queue_type, const std::s break; } default: { - ESP_LOGW(TAG, "set_nextion_sensor_state does not support a queue type %d", queue_type); + ESP_LOGW(TAG, "set_sensor_state: bad type %d", queue_type); } } } void Nextion::set_nextion_text_state(const std::string &name, const std::string &state) { - ESP_LOGD(TAG, "Received state:"); - ESP_LOGD(TAG, " variable: %s", name.c_str()); - ESP_LOGD(TAG, " state: %s", state.c_str()); + ESP_LOGD(TAG, "State: %s='%s'", name.c_str(), state.c_str()); for (auto *sensor : this->textsensortype_) { if (name == sensor->get_variable_name()) { @@ -910,7 +906,7 @@ void Nextion::set_nextion_text_state(const std::string &name, const std::string } void Nextion::all_components_send_state_(bool force_update) { - ESP_LOGD(TAG, "all_components_send_state_ "); + ESP_LOGD(TAG, "Send states"); for (auto *binarysensortype : this->binarysensortype_) { if (force_update || binarysensortype->get_needs_to_send_update()) binarysensortype->send_state_to_nextion(); @@ -998,13 +994,30 @@ uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool } /** - * @brief + * @brief Add a command to the Nextion queue that expects no response. * - * @param variable_name Name for the queue + * This is typically used for write-only operations such as variable assignments or component updates + * where no return value or acknowledgment is expected from the display. + * + * If the `max_queue_size` limit is configured and reached, the command will be skipped. + * + * @param variable_name Name of the variable or component associated with the command. */ void Nextion::add_no_result_to_queue_(const std::string &variable_name) { - // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) - nextion::NextionQueue *nextion_queue = new nextion::NextionQueue; +#ifdef USE_NEXTION_MAX_QUEUE_SIZE + if (this->max_queue_size_ > 0 && this->nextion_queue_.size() >= this->max_queue_size_) { + ESP_LOGW(TAG, "Queue full (%zu), drop: %s", this->nextion_queue_.size(), variable_name.c_str()); + return; + } +#endif + + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + nextion::NextionQueue *nextion_queue = allocator.allocate(1); + if (nextion_queue == nullptr) { + ESP_LOGW(TAG, "Queue alloc failed"); + return; + } + new (nextion_queue) nextion::NextionQueue(); // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) nextion_queue->component = new nextion::NextionComponentBase; @@ -1014,7 +1027,7 @@ void Nextion::add_no_result_to_queue_(const std::string &variable_name) { this->nextion_queue_.push_back(nextion_queue); - ESP_LOGN(TAG, "Add to queue type: NORESULT component %s", nextion_queue->component->get_variable_name().c_str()); + ESP_LOGN(TAG, "Queue NORESULT: %s", nextion_queue->component->get_variable_name().c_str()); } /** @@ -1043,7 +1056,7 @@ bool Nextion::add_no_result_to_queue_with_ignore_sleep_printf_(const std::string int ret = vsnprintf(buffer, sizeof(buffer), format, arg); va_end(arg); if (ret <= 0) { - ESP_LOGW(TAG, "Building command for format '%s' failed!", format); + ESP_LOGW(TAG, "Bad cmd format: '%s'", format); return false; } @@ -1068,7 +1081,7 @@ bool Nextion::add_no_result_to_queue_with_printf_(const std::string &variable_na int ret = vsnprintf(buffer, sizeof(buffer), format, arg); va_end(arg); if (ret <= 0) { - ESP_LOGW(TAG, "Building command for format '%s' failed!", format); + ESP_LOGW(TAG, "Bad cmd format: '%s'", format); return false; } @@ -1133,18 +1146,39 @@ void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &varia state_value.c_str()); } +/** + * @brief Queue a GET command for a component that expects a response from the Nextion display. + * + * This method is used for querying values such as sensor states, text content, or switch status. + * The component will be added to the Nextion queue only if the display is already set up, + * the queue has not reached the configured maximum size (if set), and the command is sent successfully. + * + * @param component Pointer to the Nextion component that will handle the response. + */ void Nextion::add_to_get_queue(NextionComponentBase *component) { if ((!this->is_setup() && !this->ignore_is_setup_)) return; - // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) - nextion::NextionQueue *nextion_queue = new nextion::NextionQueue; +#ifdef USE_NEXTION_MAX_QUEUE_SIZE + if (this->max_queue_size_ > 0 && this->nextion_queue_.size() >= this->max_queue_size_) { + ESP_LOGW(TAG, "Queue full (%zu), drop GET: %s", this->nextion_queue_.size(), + component->get_variable_name().c_str()); + return; + } +#endif + + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + nextion::NextionQueue *nextion_queue = allocator.allocate(1); + if (nextion_queue == nullptr) { + ESP_LOGW(TAG, "Queue alloc failed"); + return; + } + new (nextion_queue) nextion::NextionQueue(); nextion_queue->component = component; nextion_queue->queue_time = millis(); - ESP_LOGN(TAG, "Add to queue type: %s component %s", component->get_queue_type_string().c_str(), - component->get_variable_name().c_str()); + ESP_LOGN(TAG, "Queue %s: %s", component->get_queue_type_string().c_str(), component->get_variable_name().c_str()); std::string command = "get " + component->get_variable_name_to_send(); @@ -1165,8 +1199,13 @@ void Nextion::add_addt_command_to_queue(NextionComponentBase *component) { if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping()) return; - // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) - nextion::NextionQueue *nextion_queue = new nextion::NextionQueue; + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + nextion::NextionQueue *nextion_queue = allocator.allocate(1); + if (nextion_queue == nullptr) { + ESP_LOGW(TAG, "Queue alloc failed"); + return; + } + new (nextion_queue) nextion::NextionQueue(); nextion_queue->component = component; nextion_queue->queue_time = millis(); @@ -1195,8 +1234,8 @@ void Nextion::check_pending_waveform_() { void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = writer; } -ESPDEPRECATED("set_wait_for_ack(bool) is deprecated and has no effect", "v1.20") -void Nextion::set_wait_for_ack(bool wait_for_ack) { ESP_LOGE(TAG, "This command is deprecated"); } +ESPDEPRECATED("set_wait_for_ack(bool) deprecated, no effect", "v1.20") +void Nextion::set_wait_for_ack(bool wait_for_ack) { ESP_LOGE(TAG, "Deprecated"); } bool Nextion::is_updating() { return this->is_updating_; } diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 4bc5305923..036fbe6c6d 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -75,6 +75,36 @@ class NextionCommandPacer { class Nextion : public NextionBase, public PollingComponent, public uart::UARTDevice { public: +#ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP + /** + * @brief Set the maximum number of commands to process in each loop iteration + * @param value Maximum number of commands (default: 20) + * + * Limiting the number of commands per loop helps prevent stack overflows + * when a large number of commands are queued at once, especially during boot. + */ + inline void set_max_commands_per_loop(uint16_t value) { this->max_commands_per_loop_ = value; } + + /** + * @brief Get the current maximum number of commands allowed per loop iteration + * @return Configured command limit per loop + */ + inline uint16_t get_max_commands_per_loop() const { return this->max_commands_per_loop_; } +#endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP +#ifdef USE_NEXTION_MAX_QUEUE_SIZE + /** + * @brief Set the maximum allowed queue size + * @param size Max number of entries allowed in nextion_queue_ + */ + inline void set_max_queue_size(size_t size) { this->max_queue_size_ = size; } + + /** + * @brief Get the maximum allowed queue size + * @return Current limit (0 = unlimited) + */ + inline size_t get_max_queue_size() const { return this->max_queue_size_; } +#endif // USE_NEXTION_MAX_QUEUE_SIZE + #ifdef USE_NEXTION_COMMAND_SPACING /** * @brief Set the command spacing for the display @@ -1273,6 +1303,12 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe bool is_connected() { return this->is_connected_; } protected: +#ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP + uint16_t max_commands_per_loop_{1000}; +#endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP +#ifdef USE_NEXTION_MAX_QUEUE_SIZE + size_t max_queue_size_{0}; +#endif // USE_NEXTION_MAX_QUEUE_SIZE #ifdef USE_NEXTION_COMMAND_SPACING NextionCommandPacer command_pacer_{0}; #endif // USE_NEXTION_COMMAND_SPACING diff --git a/esphome/components/nextion/nextion_commands.cpp b/esphome/components/nextion/nextion_commands.cpp index e3172c8c1b..0226e0a13c 100644 --- a/esphome/components/nextion/nextion_commands.cpp +++ b/esphome/components/nextion/nextion_commands.cpp @@ -17,7 +17,7 @@ void Nextion::set_wake_up_page(uint8_t wake_up_page) { void Nextion::set_touch_sleep_timeout(uint32_t touch_sleep_timeout) { if (touch_sleep_timeout < 3) { - ESP_LOGD(TAG, "Sleep timeout out of bounds, range 3-65535"); + ESP_LOGD(TAG, "Sleep timeout out of bounds (3-65535)"); return; } @@ -37,7 +37,7 @@ void Nextion::sleep(bool sleep) { // Protocol reparse mode bool Nextion::set_protocol_reparse_mode(bool active_mode) { - ESP_LOGV(TAG, "Set Nextion protocol reparse mode: %s", YESNO(active_mode)); + ESP_LOGV(TAG, "Reparse mode: %s", YESNO(active_mode)); this->ignore_is_setup_ = true; // if not in reparse mode setup will fail, so it should be ignored bool all_commands_sent = true; if (active_mode) { // Sets active protocol reparse mode @@ -184,7 +184,7 @@ void Nextion::goto_page(uint8_t page) { this->add_no_result_to_queue_with_printf void Nextion::set_backlight_brightness(float brightness) { if (brightness < 0 || brightness > 1.0) { - ESP_LOGD(TAG, "Brightness out of bounds, percentage range 0-1.0"); + ESP_LOGD(TAG, "Brightness out of bounds (0-1.0)"); return; } this->add_no_result_to_queue_with_printf_("backlight_brightness", "dim=%d", static_cast(brightness * 100)); diff --git a/esphome/components/nextion/nextion_upload_arduino.cpp b/esphome/components/nextion/nextion_upload_arduino.cpp index 1187c77c8e..6652e70172 100644 --- a/esphome/components/nextion/nextion_upload_arduino.cpp +++ b/esphome/components/nextion/nextion_upload_arduino.cpp @@ -31,7 +31,7 @@ inline uint32_t Nextion::get_free_heap_() { int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { uint32_t range_size = this->tft_size_ - range_start; - ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_()); + ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_()); uint32_t range_end = ((upload_first_chunk_sent_ or this->tft_size_ < 4096) ? this->tft_size_ : 4096) - 1; ESP_LOGD(TAG, "Range start: %" PRIu32, range_start); if (range_size <= 0 or range_end <= range_start) { @@ -43,11 +43,11 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { char range_header[32]; sprintf(range_header, "bytes=%" PRIu32 "-%" PRIu32, range_start, range_end); - ESP_LOGV(TAG, "Requesting range: %s", range_header); + ESP_LOGV(TAG, "Range: %s", range_header); http_client.addHeader("Range", range_header); int code = http_client.GET(); if (code != HTTP_CODE_OK and code != HTTP_CODE_PARTIAL_CONTENT) { - ESP_LOGW(TAG, "HTTP Request failed; Error: %s", HTTPClient::errorToString(code).c_str()); + ESP_LOGW(TAG, "HTTP failed: %s", HTTPClient::errorToString(code).c_str()); return -1; } @@ -55,7 +55,7 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); uint8_t *buffer = allocator.allocate(4096); if (!buffer) { - ESP_LOGE(TAG, "Failed to allocate upload buffer"); + ESP_LOGE(TAG, "Buffer alloc failed"); return -1; } @@ -64,7 +64,7 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { App.feed_wdt(); const uint16_t buffer_size = this->content_length_ < 4096 ? this->content_length_ : 4096; // Limits buffer to the remaining data - ESP_LOGV(TAG, "Fetching %" PRIu16 " bytes from HTTP", buffer_size); + ESP_LOGV(TAG, "Fetch %" PRIu16 " bytes", buffer_size); uint16_t read_len = 0; int partial_read_len = 0; const uint32_t start_time = millis(); @@ -81,14 +81,13 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { } if (read_len != buffer_size) { // Did not receive the full package within the timeout period - ESP_LOGE(TAG, "Failed to read full package, received only %" PRIu16 " of %" PRIu16 " bytes", read_len, - buffer_size); + ESP_LOGE(TAG, "Read failed: %" PRIu16 "/%" PRIu16 " bytes", read_len, buffer_size); // Deallocate buffer allocator.deallocate(buffer, 4096); buffer = nullptr; return -1; } - ESP_LOGV(TAG, "%d bytes fetched, writing it to UART", read_len); + ESP_LOGV(TAG, "Fetched %d bytes", read_len); if (read_len > 0) { recv_string.clear(); this->write_array(buffer, buffer_size); @@ -97,25 +96,23 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { this->content_length_ -= read_len; const float upload_percentage = 100.0f * (this->tft_size_ - this->content_length_) / this->tft_size_; #if defined(USE_ESP32) && defined(USE_PSRAM) - ESP_LOGD( - TAG, - "Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " (DRAM) + %" PRIu32 " (PSRAM) bytes", - upload_percentage, this->content_length_, static_cast(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)), - static_cast(heap_caps_get_free_size(MALLOC_CAP_SPIRAM))); + ESP_LOGD(TAG, "Upload: %0.2f%% (%" PRIu32 " left, heap: %" PRIu32 "+%" PRIu32 ")", upload_percentage, + this->content_length_, static_cast(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)), + static_cast(heap_caps_get_free_size(MALLOC_CAP_SPIRAM))); #else - ESP_LOGD(TAG, "Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " bytes", upload_percentage, - this->content_length_, this->get_free_heap_()); + ESP_LOGD(TAG, "Upload: %0.2f%% (%" PRIu32 " left, heap: %" PRIu32 ")", upload_percentage, this->content_length_, + this->get_free_heap_()); #endif upload_first_chunk_sent_ = true; if (recv_string[0] == 0x08 && recv_string.size() == 5) { // handle partial upload request - ESP_LOGD(TAG, "recv_string [%s]", + ESP_LOGD(TAG, "Recv: [%s]", format_hex_pretty(reinterpret_cast(recv_string.data()), recv_string.size()).c_str()); uint32_t result = 0; for (int j = 0; j < 4; ++j) { result += static_cast(recv_string[j + 1]) << (8 * j); } if (result > 0) { - ESP_LOGI(TAG, "Nextion reported new range %" PRIu32, result); + ESP_LOGI(TAG, "New range: %" PRIu32, result); this->content_length_ = this->tft_size_ - result; range_start = result; } else { @@ -126,7 +123,7 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { buffer = nullptr; return range_end + 1; } else if (recv_string[0] != 0x05 and recv_string[0] != 0x08) { // 0x05 == "ok" - ESP_LOGE(TAG, "Invalid response from Nextion: [%s]", + ESP_LOGE(TAG, "Invalid response: [%s]", format_hex_pretty(reinterpret_cast(recv_string.data()), recv_string.size()).c_str()); // Deallocate buffer allocator.deallocate(buffer, 4096); @@ -136,10 +133,10 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { recv_string.clear(); } else if (read_len == 0) { - ESP_LOGV(TAG, "End of HTTP response reached"); + ESP_LOGV(TAG, "HTTP end"); break; // Exit the loop if there is no more data to read } else { - ESP_LOGE(TAG, "Failed to read from HTTP client, error code: %d", read_len); + ESP_LOGE(TAG, "HTTP read failed: %d", read_len); break; // Exit the loop on error } } @@ -151,26 +148,26 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) { } bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { - ESP_LOGD(TAG, "Nextion TFT upload requested"); + ESP_LOGD(TAG, "TFT upload requested"); ESP_LOGD(TAG, "Exit reparse: %s", YESNO(exit_reparse)); ESP_LOGD(TAG, "URL: %s", this->tft_url_.c_str()); if (this->is_updating_) { - ESP_LOGW(TAG, "Currently uploading"); + ESP_LOGW(TAG, "Upload in progress"); return false; } if (!network::is_connected()) { - ESP_LOGE(TAG, "Network is not connected"); + ESP_LOGE(TAG, "No network"); return false; } this->is_updating_ = true; if (exit_reparse) { - ESP_LOGD(TAG, "Exiting Nextion reparse mode"); + ESP_LOGD(TAG, "Exit reparse mode"); if (!this->set_protocol_reparse_mode(false)) { - ESP_LOGW(TAG, "Failed to request Nextion to exit reparse mode"); + ESP_LOGW(TAG, "Exit reparse failed"); return false; } } @@ -185,8 +182,8 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { ESP_LOGD(TAG, "Baud rate: %" PRIu32, baud_rate); // Define the configuration for the HTTP client - ESP_LOGV(TAG, "Initializing HTTP client"); - ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_()); + ESP_LOGV(TAG, "Init HTTP client"); + ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_()); HTTPClient http_client; http_client.setTimeout(15000); // Yes 15 seconds.... Helps 8266s along @@ -215,7 +212,7 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { http_client.addHeader("Range", "bytes=0-255"); const char *header_names[] = {"Content-Range"}; http_client.collectHeaders(header_names, 1); - ESP_LOGD(TAG, "Requesting URL: %s", this->tft_url_.c_str()); + ESP_LOGD(TAG, "URL: %s", this->tft_url_.c_str()); http_client.setReuse(true); // try up to 5 times. DNS sometimes needs a second try or so int tries = 1; @@ -224,7 +221,7 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { App.feed_wdt(); while (code != 200 && code != 206 && tries <= 5) { - ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s, retrying (%d/5)", this->tft_url_.c_str(), + ESP_LOGW(TAG, "HTTP fail: URL: %s; Error: %s, retry %d/5", this->tft_url_.c_str(), HTTPClient::errorToString(code).c_str(), tries); delay(250); // NOLINT @@ -241,27 +238,27 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { content_range_string.remove(0, 12); this->tft_size_ = content_range_string.toInt(); - ESP_LOGD(TAG, "TFT file size: %zu bytes", this->tft_size_); + ESP_LOGD(TAG, "TFT size: %zu bytes", this->tft_size_); if (this->tft_size_ < 4096) { - ESP_LOGE(TAG, "File size check failed."); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGE(TAG, "Size check failed"); + ESP_LOGD(TAG, "Close HTTP"); http_client.end(); ESP_LOGV(TAG, "Connection closed"); return this->upload_end_(false); } else { - ESP_LOGV(TAG, "File size check passed. Proceeding..."); + ESP_LOGV(TAG, "Size check OK"); } this->content_length_ = this->tft_size_; - ESP_LOGD(TAG, "Uploading Nextion"); + ESP_LOGD(TAG, "Uploading"); // The Nextion will ignore the upload command if it is sleeping - ESP_LOGV(TAG, "Wake-up Nextion"); + ESP_LOGV(TAG, "Wake-up"); this->ignore_is_setup_ = true; this->send_command_("sleep=0"); this->send_command_("dim=100"); delay(250); // NOLINT - ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_()); + ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_()); App.feed_wdt(); char command[128]; @@ -271,16 +268,16 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { sprintf(command, "whmi-wris %d,%d,1", this->content_length_, baud_rate); // Clear serial receive buffer - ESP_LOGV(TAG, "Clear serial receive buffer"); + ESP_LOGV(TAG, "Clear RX buffer"); this->reset_(false); delay(250); // NOLINT - ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_()); + ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_()); - ESP_LOGV(TAG, "Send upload instruction: %s", command); + ESP_LOGV(TAG, "Upload cmd: %s", command); this->send_command_(command); if (baud_rate != this->original_baud_rate_) { - ESP_LOGD(TAG, "Changing baud rate from %" PRIu32 " to %" PRIu32 " bps", this->original_baud_rate_, baud_rate); + ESP_LOGD(TAG, "Baud: %" PRIu32 "->%" PRIu32, this->original_baud_rate_, baud_rate); this->parent_->set_baud_rate(baud_rate); this->parent_->load_settings(); } @@ -288,75 +285,78 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { App.feed_wdt(); std::string response; - ESP_LOGV(TAG, "Waiting for upgrade response"); + ESP_LOGV(TAG, "Wait upload resp"); this->recv_ret_string_(response, 5000, true); // This can take some time to return // The Nextion display will, if it's ready to accept data, send a 0x05 byte. - ESP_LOGD(TAG, "Upgrade response is [%s] - %zu byte(s)", + ESP_LOGD(TAG, "Upload resp: [%s] %zu B", format_hex_pretty(reinterpret_cast(response.data()), response.size()).c_str(), response.length()); - ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_()); + ESP_LOGV(TAG, "Heap: %" PRIu32, this->get_free_heap_()); if (response.find(0x05) != std::string::npos) { - ESP_LOGV(TAG, "Preparation for TFT upload done"); + ESP_LOGV(TAG, "Upload prep done"); } else { - ESP_LOGE(TAG, "Preparation for TFT upload failed %d \"%s\"", response[0], response.c_str()); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGE(TAG, "Prep failed %d '%s'", response[0], response.c_str()); + ESP_LOGD(TAG, "Close HTTP"); http_client.end(); ESP_LOGV(TAG, "Connection closed"); return this->upload_end_(false); } - ESP_LOGD(TAG, "Uploading TFT to Nextion:"); - ESP_LOGD(TAG, " URL: %s", this->tft_url_.c_str()); - ESP_LOGD(TAG, " File size: %d bytes", this->content_length_); - ESP_LOGD(TAG, " Free heap: %" PRIu32, this->get_free_heap_()); + ESP_LOGD(TAG, "Upload TFT:"); + ESP_LOGD(TAG, " URL: %s", this->tft_url_.c_str()); + ESP_LOGD(TAG, " Size: %d bytes", this->content_length_); + ESP_LOGD(TAG, " Heap: %" PRIu32, this->get_free_heap_()); // Proceed with the content download as before - ESP_LOGV(TAG, "Starting transfer by chunks loop"); + ESP_LOGV(TAG, "Start chunk transfer"); uint32_t position = 0; while (this->content_length_ > 0) { int upload_result = upload_by_chunks_(http_client, position); if (upload_result < 0) { - ESP_LOGE(TAG, "Error uploading TFT to Nextion!"); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGE(TAG, "Upload error"); + ESP_LOGD(TAG, "Close HTTP"); http_client.end(); ESP_LOGV(TAG, "Connection closed"); return this->upload_end_(false); } App.feed_wdt(); - ESP_LOGV(TAG, "Free heap: %" PRIu32 ", Bytes left: %" PRIu32, this->get_free_heap_(), this->content_length_); + ESP_LOGV(TAG, "Heap: %" PRIu32 " left: %" PRIu32, this->get_free_heap_(), this->content_length_); } - ESP_LOGD(TAG, "Successfully uploaded TFT to Nextion!"); + ESP_LOGD(TAG, "Upload complete"); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGV(TAG, "Close HTTP"); http_client.end(); ESP_LOGV(TAG, "Connection closed"); return upload_end_(true); } bool Nextion::upload_end_(bool successful) { - ESP_LOGD(TAG, "Nextion TFT upload finished: %s", YESNO(successful)); - this->is_updating_ = false; - this->ignore_is_setup_ = false; - - uint32_t baud_rate = this->parent_->get_baud_rate(); - if (baud_rate != this->original_baud_rate_) { - ESP_LOGD(TAG, "Changing baud rate back from %" PRIu32 " to %" PRIu32 " bps", baud_rate, this->original_baud_rate_); - this->parent_->set_baud_rate(this->original_baud_rate_); - this->parent_->load_settings(); - } + ESP_LOGD(TAG, "TFT upload done: %s", YESNO(successful)); if (successful) { - ESP_LOGD(TAG, "Restarting ESPHome"); + ESP_LOGD(TAG, "Restart"); + delay(1500); // NOLINT + App.safe_reboot(); delay(1500); // NOLINT - arch_restart(); } else { - ESP_LOGE(TAG, "Nextion TFT upload failed"); + ESP_LOGE(TAG, "TFT upload failed"); + + this->is_updating_ = false; + this->ignore_is_setup_ = false; + + uint32_t baud_rate = this->parent_->get_baud_rate(); + if (baud_rate != this->original_baud_rate_) { + ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_); + this->parent_->set_baud_rate(this->original_baud_rate_); + this->parent_->load_settings(); + } } + return successful; } diff --git a/esphome/components/nextion/nextion_upload_idf.cpp b/esphome/components/nextion/nextion_upload_idf.cpp index 7541a57d56..fc98056bc3 100644 --- a/esphome/components/nextion/nextion_upload_idf.cpp +++ b/esphome/components/nextion/nextion_upload_idf.cpp @@ -21,7 +21,7 @@ static const char *const TAG = "nextion.upload.idf"; int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &range_start) { uint32_t range_size = this->tft_size_ - range_start; - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); uint32_t range_end = ((upload_first_chunk_sent_ or this->tft_size_ < 4096) ? this->tft_size_ : 4096) - 1; ESP_LOGD(TAG, "Range start: %" PRIu32, range_start); if (range_size <= 0 or range_end <= range_start) { @@ -33,20 +33,20 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r char range_header[32]; sprintf(range_header, "bytes=%" PRIu32 "-%" PRIu32, range_start, range_end); - ESP_LOGV(TAG, "Requesting range: %s", range_header); + ESP_LOGV(TAG, "Range: %s", range_header); esp_http_client_set_header(http_client, "Range", range_header); - ESP_LOGV(TAG, "Opening HTTP connetion"); + ESP_LOGV(TAG, "Open HTTP"); esp_err_t err = esp_http_client_open(http_client, 0); if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "HTTP open failed: %s", esp_err_to_name(err)); return -1; } - ESP_LOGV(TAG, "Fetch content length"); + ESP_LOGV(TAG, "Fetch length"); const int chunk_size = esp_http_client_fetch_headers(http_client); - ESP_LOGV(TAG, "content_length = %d", chunk_size); + ESP_LOGV(TAG, "Length: %d", chunk_size); if (chunk_size <= 0) { - ESP_LOGE(TAG, "Failed to get chunk's content length: %d", chunk_size); + ESP_LOGE(TAG, "Get length failed: %d", chunk_size); return -1; } @@ -54,7 +54,7 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); uint8_t *buffer = allocator.allocate(4096); if (!buffer) { - ESP_LOGE(TAG, "Failed to allocate upload buffer"); + ESP_LOGE(TAG, "Buffer alloc failed"); return -1; } @@ -63,7 +63,7 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r App.feed_wdt(); const uint16_t buffer_size = this->content_length_ < 4096 ? this->content_length_ : 4096; // Limits buffer to the remaining data - ESP_LOGV(TAG, "Fetching %" PRIu16 " bytes from HTTP", buffer_size); + ESP_LOGV(TAG, "Fetch %" PRIu16 " bytes", buffer_size); uint16_t read_len = 0; int partial_read_len = 0; uint8_t retries = 0; @@ -84,14 +84,13 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r } if (read_len != buffer_size) { // Did not receive the full package within the timeout period - ESP_LOGE(TAG, "Failed to read full package, received only %" PRIu16 " of %" PRIu16 " bytes", read_len, - buffer_size); + ESP_LOGE(TAG, "Read failed: %" PRIu16 "/%" PRIu16 " bytes", read_len, buffer_size); // Deallocate buffer allocator.deallocate(buffer, 4096); buffer = nullptr; return -1; } - ESP_LOGV(TAG, "%d bytes fetched, writing it to UART", read_len); + ESP_LOGV(TAG, "Fetched %d bytes", read_len); if (read_len > 0) { recv_string.clear(); this->write_array(buffer, buffer_size); @@ -100,25 +99,23 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r this->content_length_ -= read_len; const float upload_percentage = 100.0f * (this->tft_size_ - this->content_length_) / this->tft_size_; #ifdef USE_PSRAM - ESP_LOGD( - TAG, - "Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " (DRAM) + %" PRIu32 " (PSRAM) bytes", - upload_percentage, this->content_length_, static_cast(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)), - static_cast(heap_caps_get_free_size(MALLOC_CAP_SPIRAM))); + ESP_LOGD(TAG, "Upload: %0.2f%% (%" PRIu32 " left, heap: %" PRIu32 "+%" PRIu32 ")", upload_percentage, + this->content_length_, static_cast(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)), + static_cast(heap_caps_get_free_size(MALLOC_CAP_SPIRAM))); #else - ESP_LOGD(TAG, "Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " bytes", upload_percentage, - this->content_length_, static_cast(esp_get_free_heap_size())); + ESP_LOGD(TAG, "Upload: %0.2f%% (%" PRIu32 " left, heap: %" PRIu32 ")", upload_percentage, this->content_length_, + static_cast(esp_get_free_heap_size())); #endif upload_first_chunk_sent_ = true; if (recv_string[0] == 0x08 && recv_string.size() == 5) { // handle partial upload request - ESP_LOGD(TAG, "recv_string [%s]", + ESP_LOGD(TAG, "Recv: [%s]", format_hex_pretty(reinterpret_cast(recv_string.data()), recv_string.size()).c_str()); uint32_t result = 0; for (int j = 0; j < 4; ++j) { result += static_cast(recv_string[j + 1]) << (8 * j); } if (result > 0) { - ESP_LOGI(TAG, "Nextion reported new range %" PRIu32, result); + ESP_LOGI(TAG, "New range: %" PRIu32, result); this->content_length_ = this->tft_size_ - result; range_start = result; } else { @@ -129,7 +126,7 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r buffer = nullptr; return range_end + 1; } else if (recv_string[0] != 0x05 and recv_string[0] != 0x08) { // 0x05 == "ok" - ESP_LOGE(TAG, "Invalid response from Nextion: [%s]", + ESP_LOGE(TAG, "Invalid response: [%s]", format_hex_pretty(reinterpret_cast(recv_string.data()), recv_string.size()).c_str()); // Deallocate buffer allocator.deallocate(buffer, 4096); @@ -139,10 +136,10 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r recv_string.clear(); } else if (read_len == 0) { - ESP_LOGV(TAG, "End of HTTP response reached"); + ESP_LOGV(TAG, "HTTP end"); break; // Exit the loop if there is no more data to read } else { - ESP_LOGE(TAG, "Failed to read from HTTP client, error code: %" PRIu16, read_len); + ESP_LOGE(TAG, "HTTP read failed: %" PRIu16, read_len); break; // Exit the loop on error } } @@ -154,26 +151,26 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r } bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { - ESP_LOGD(TAG, "Nextion TFT upload requested"); + ESP_LOGD(TAG, "TFT upload requested"); ESP_LOGD(TAG, "Exit reparse: %s", YESNO(exit_reparse)); ESP_LOGD(TAG, "URL: %s", this->tft_url_.c_str()); if (this->is_updating_) { - ESP_LOGW(TAG, "Currently uploading"); + ESP_LOGW(TAG, "Upload in progress"); return false; } if (!network::is_connected()) { - ESP_LOGE(TAG, "Network is not connected"); + ESP_LOGE(TAG, "No network"); return false; } this->is_updating_ = true; if (exit_reparse) { - ESP_LOGD(TAG, "Exiting Nextion reparse mode"); + ESP_LOGD(TAG, "Exit reparse mode"); if (!this->set_protocol_reparse_mode(false)) { - ESP_LOGW(TAG, "Failed to request Nextion to exit reparse mode"); + ESP_LOGW(TAG, "Exit reparse failed"); return false; } } @@ -188,8 +185,8 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { ESP_LOGD(TAG, "Baud rate: %" PRIu32, baud_rate); // Define the configuration for the HTTP client - ESP_LOGV(TAG, "Initializing HTTP client"); - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Init HTTP client"); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); esp_http_client_config_t config = { .url = this->tft_url_.c_str(), .cert_pem = nullptr, @@ -201,30 +198,30 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { // Initialize the HTTP client with the configuration esp_http_client_handle_t http_client = esp_http_client_init(&config); if (!http_client) { - ESP_LOGE(TAG, "Failed to initialize HTTP client."); + ESP_LOGE(TAG, "HTTP init failed"); return this->upload_end_(false); } esp_err_t err = esp_http_client_set_header(http_client, "Connection", "keep-alive"); if (err != ESP_OK) { - ESP_LOGE(TAG, "HTTP set header failed: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Set header failed: %s", esp_err_to_name(err)); esp_http_client_cleanup(http_client); return this->upload_end_(false); } // Perform the HTTP request - ESP_LOGV(TAG, "Check if the client could connect"); - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Check connection"); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); err = esp_http_client_perform(http_client); if (err != ESP_OK) { - ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "HTTP failed: %s", esp_err_to_name(err)); esp_http_client_cleanup(http_client); return this->upload_end_(false); } // Check the HTTP Status Code - ESP_LOGV(TAG, "Check the HTTP Status Code"); - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Check status"); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); int status_code = esp_http_client_get_status_code(http_client); if (status_code != 200 && status_code != 206) { return this->upload_end_(false); @@ -232,28 +229,28 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { this->tft_size_ = esp_http_client_get_content_length(http_client); - ESP_LOGD(TAG, "TFT file size: %zu bytes", this->tft_size_); + ESP_LOGD(TAG, "TFT size: %zu bytes", this->tft_size_); if (this->tft_size_ < 4096 || this->tft_size_ > 134217728) { - ESP_LOGE(TAG, "File size check failed."); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGE(TAG, "Size check failed"); + ESP_LOGD(TAG, "Close HTTP"); esp_http_client_close(http_client); esp_http_client_cleanup(http_client); ESP_LOGV(TAG, "Connection closed"); return this->upload_end_(false); } else { - ESP_LOGV(TAG, "File size check passed. Proceeding..."); + ESP_LOGV(TAG, "Size check OK"); } this->content_length_ = this->tft_size_; - ESP_LOGD(TAG, "Uploading Nextion"); + ESP_LOGD(TAG, "Uploading"); // The Nextion will ignore the upload command if it is sleeping - ESP_LOGV(TAG, "Wake-up Nextion"); + ESP_LOGV(TAG, "Wake-up"); this->ignore_is_setup_ = true; this->send_command_("sleep=0"); this->send_command_("dim=100"); vTaskDelay(pdMS_TO_TICKS(250)); // NOLINT - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); App.feed_wdt(); char command[128]; @@ -263,75 +260,75 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { sprintf(command, "whmi-wris %" PRIu32 ",%" PRIu32 ",1", this->content_length_, baud_rate); // Clear serial receive buffer - ESP_LOGV(TAG, "Clear serial receive buffer"); + ESP_LOGV(TAG, "Clear RX buffer"); this->reset_(false); vTaskDelay(pdMS_TO_TICKS(250)); // NOLINT - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); - ESP_LOGV(TAG, "Send upload instruction: %s", command); + ESP_LOGV(TAG, "Upload cmd: %s", command); this->send_command_(command); if (baud_rate != this->original_baud_rate_) { - ESP_LOGD(TAG, "Changing baud rate from %" PRIu32 " to %" PRIu32 " bps", this->original_baud_rate_, baud_rate); + ESP_LOGD(TAG, "Baud: %" PRIu32 "->%" PRIu32, this->original_baud_rate_, baud_rate); this->parent_->set_baud_rate(baud_rate); this->parent_->load_settings(); } std::string response; - ESP_LOGV(TAG, "Waiting for upgrade response"); + ESP_LOGV(TAG, "Wait upload resp"); this->recv_ret_string_(response, 5000, true); // This can take some time to return // The Nextion display will, if it's ready to accept data, send a 0x05 byte. - ESP_LOGD(TAG, "Upgrade response is [%s] - %zu byte(s)", + ESP_LOGD(TAG, "Upload resp: [%s] %zu B", format_hex_pretty(reinterpret_cast(response.data()), response.size()).c_str(), response.length()); - ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGV(TAG, "Heap: %" PRIu32, esp_get_free_heap_size()); if (response.find(0x05) != std::string::npos) { - ESP_LOGV(TAG, "Preparation for TFT upload done"); + ESP_LOGV(TAG, "Upload prep done"); } else { - ESP_LOGE(TAG, "Preparation for TFT upload failed %d \"%s\"", response[0], response.c_str()); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGE(TAG, "Upload prep failed %d '%s'", response[0], response.c_str()); + ESP_LOGD(TAG, "Close HTTP"); esp_http_client_close(http_client); esp_http_client_cleanup(http_client); ESP_LOGV(TAG, "Connection closed"); return this->upload_end_(false); } - ESP_LOGV(TAG, "Change the method to GET before starting the download"); + ESP_LOGV(TAG, "Set method to GET"); esp_err_t set_method_result = esp_http_client_set_method(http_client, HTTP_METHOD_GET); if (set_method_result != ESP_OK) { - ESP_LOGE(TAG, "Failed to set HTTP method to GET: %s", esp_err_to_name(set_method_result)); + ESP_LOGE(TAG, "Set GET failed: %s", esp_err_to_name(set_method_result)); return this->upload_end_(false); } - ESP_LOGD(TAG, "Uploading TFT to Nextion:"); - ESP_LOGD(TAG, " URL: %s", this->tft_url_.c_str()); - ESP_LOGD(TAG, " File size: %" PRIu32 " bytes", this->content_length_); - ESP_LOGD(TAG, " Free heap: %" PRIu32, esp_get_free_heap_size()); + ESP_LOGD(TAG, "Uploading TFT:"); + ESP_LOGD(TAG, " URL: %s", this->tft_url_.c_str()); + ESP_LOGD(TAG, " Size: %" PRIu32 " bytes", this->content_length_); + ESP_LOGD(TAG, " Heap: %" PRIu32, esp_get_free_heap_size()); // Proceed with the content download as before - ESP_LOGV(TAG, "Starting transfer by chunks loop"); + ESP_LOGV(TAG, "Start chunk transfer"); uint32_t position = 0; while (this->content_length_ > 0) { int upload_result = upload_by_chunks_(http_client, position); if (upload_result < 0) { - ESP_LOGE(TAG, "Error uploading TFT to Nextion!"); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGE(TAG, "TFT upload error"); + ESP_LOGD(TAG, "Close HTTP"); esp_http_client_close(http_client); esp_http_client_cleanup(http_client); ESP_LOGV(TAG, "Connection closed"); return this->upload_end_(false); } App.feed_wdt(); - ESP_LOGV(TAG, "Free heap: %" PRIu32 ", Bytes left: %" PRIu32, esp_get_free_heap_size(), this->content_length_); + ESP_LOGV(TAG, "Heap: %" PRIu32 " left: %" PRIu32, esp_get_free_heap_size(), this->content_length_); } - ESP_LOGD(TAG, "Successfully uploaded TFT to Nextion!"); + ESP_LOGD(TAG, "TFT upload complete"); - ESP_LOGD(TAG, "Close HTTP connection"); + ESP_LOGD(TAG, "Close HTTP"); esp_http_client_close(http_client); esp_http_client_cleanup(http_client); ESP_LOGV(TAG, "Connection closed"); @@ -339,24 +336,26 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { } bool Nextion::upload_end_(bool successful) { - ESP_LOGD(TAG, "Nextion TFT upload finished: %s", YESNO(successful)); - this->is_updating_ = false; - this->ignore_is_setup_ = false; - - uint32_t baud_rate = this->parent_->get_baud_rate(); - if (baud_rate != this->original_baud_rate_) { - ESP_LOGD(TAG, "Changing baud rate back from %" PRIu32 " to %" PRIu32 " bps", baud_rate, this->original_baud_rate_); - this->parent_->set_baud_rate(this->original_baud_rate_); - this->parent_->load_settings(); - } + ESP_LOGD(TAG, "TFT upload done: %s", YESNO(successful)); if (successful) { - ESP_LOGD(TAG, "Restarting ESPHome"); + ESP_LOGD(TAG, "Restart"); delay(1500); // NOLINT - arch_restart(); + App.safe_reboot(); } else { - ESP_LOGE(TAG, "Nextion TFT upload failed"); + ESP_LOGE(TAG, "TFT upload failed"); + + this->is_updating_ = false; + this->ignore_is_setup_ = false; + + uint32_t baud_rate = this->parent_->get_baud_rate(); + if (baud_rate != this->original_baud_rate_) { + ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_); + this->parent_->set_baud_rate(this->original_baud_rate_); + this->parent_->load_settings(); + } } + return successful; } diff --git a/esphome/components/nextion/sensor/nextion_sensor.cpp b/esphome/components/nextion/sensor/nextion_sensor.cpp index 6cc641fcf3..0ed9da95d4 100644 --- a/esphome/components/nextion/sensor/nextion_sensor.cpp +++ b/esphome/components/nextion/sensor/nextion_sensor.cpp @@ -13,7 +13,7 @@ void NextionSensor::process_sensor(const std::string &variable_name, int state) if (this->wave_chan_id_ == UINT8_MAX && this->variable_name_ == variable_name) { this->publish_state(state); - ESP_LOGD(TAG, "Processed sensor \"%s\" state %d", variable_name.c_str(), state); + ESP_LOGD(TAG, "Sensor: %s=%d", variable_name.c_str(), state); } } @@ -88,12 +88,12 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) { } else { this->raw_state = state; this->state = state; - this->has_state_ = true; + this->set_has_state(true); } } this->update_component_settings(); - ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %lf", this->variable_name_.c_str(), published_state); + ESP_LOGN(TAG, "Write: %s=%lf", this->variable_name_.c_str(), published_state); } void NextionSensor::wave_update_() { @@ -105,8 +105,8 @@ void NextionSensor::wave_update_() { size_t buffer_to_send = this->wave_buffer_.size() < 255 ? this->wave_buffer_.size() : 255; // ADDT command can only send 255 - ESP_LOGN(TAG, "wave_update send %zu of %zu value(s) to wave nextion component id %d and wave channel id %d", - buffer_to_send, this->wave_buffer_.size(), this->component_id_, this->wave_chan_id_); + ESP_LOGN(TAG, "Wave update: %zu/%zu vals to comp %d ch %d", buffer_to_send, this->wave_buffer_.size(), + this->component_id_, this->wave_chan_id_); #endif this->nextion_->add_addt_command_to_queue(this); diff --git a/esphome/components/nextion/switch/nextion_switch.cpp b/esphome/components/nextion/switch/nextion_switch.cpp index 63c1882b48..fe71182496 100644 --- a/esphome/components/nextion/switch/nextion_switch.cpp +++ b/esphome/components/nextion/switch/nextion_switch.cpp @@ -13,7 +13,7 @@ void NextionSwitch::process_bool(const std::string &variable_name, bool on) { if (this->variable_name_ == variable_name) { this->publish_state(on); - ESP_LOGD(TAG, "Processed switch \"%s\" state %s", variable_name.c_str(), state ? "ON" : "OFF"); + ESP_LOGD(TAG, "Switch: %s=%s", variable_name.c_str(), ONOFF(on)); } } @@ -43,7 +43,7 @@ void NextionSwitch::set_state(bool state, bool publish, bool send_to_nextion) { this->update_component_settings(); - ESP_LOGN(TAG, "Updated switch \"%s\" state %s", this->variable_name_.c_str(), ONOFF(state)); + ESP_LOGN(TAG, "Write: %s=%s", this->variable_name_.c_str(), ONOFF(state)); } void NextionSwitch::write_state(bool state) { this->set_state(state); } diff --git a/esphome/components/nextion/text_sensor/nextion_textsensor.cpp b/esphome/components/nextion/text_sensor/nextion_textsensor.cpp index a3fc9390f5..e08cbb02ca 100644 --- a/esphome/components/nextion/text_sensor/nextion_textsensor.cpp +++ b/esphome/components/nextion/text_sensor/nextion_textsensor.cpp @@ -11,7 +11,7 @@ void NextionTextSensor::process_text(const std::string &variable_name, const std return; if (this->variable_name_ == variable_name) { this->publish_state(text_value); - ESP_LOGD(TAG, "Processed text_sensor \"%s\" state \"%s\"", variable_name.c_str(), text_value.c_str()); + ESP_LOGD(TAG, "Text sensor: %s='%s'", variable_name.c_str(), text_value.c_str()); } } @@ -37,12 +37,12 @@ void NextionTextSensor::set_state(const std::string &state, bool publish, bool s this->publish_state(state); } else { this->state = state; - this->has_state_ = true; + this->set_has_state(true); } this->update_component_settings(); - ESP_LOGN(TAG, "Wrote state for text_sensor \"%s\" state \"%s\"", this->variable_name_.c_str(), state.c_str()); + ESP_LOGN(TAG, "Write: %s='%s'", this->variable_name_.c_str(), state.c_str()); } } // namespace nextion diff --git a/esphome/components/nfc/nci_message.h b/esphome/components/nfc/nci_message.h index c6b8537402..0c5c871f74 100644 --- a/esphome/components/nfc/nci_message.h +++ b/esphome/components/nfc/nci_message.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include diff --git a/esphome/components/nfc/ndef_record.h b/esphome/components/nfc/ndef_record.h index 20542bf24b..76d8b6a50a 100644 --- a/esphome/components/nfc/ndef_record.h +++ b/esphome/components/nfc/ndef_record.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include diff --git a/esphome/components/nfc/ndef_record_text.h b/esphome/components/nfc/ndef_record_text.h index aa8f13bb4b..e6c15704f0 100644 --- a/esphome/components/nfc/ndef_record_text.h +++ b/esphome/components/nfc/ndef_record_text.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "ndef_record.h" #include diff --git a/esphome/components/nfc/ndef_record_uri.h b/esphome/components/nfc/ndef_record_uri.h index fc8f2d9a73..1eadda1b4f 100644 --- a/esphome/components/nfc/ndef_record_uri.h +++ b/esphome/components/nfc/ndef_record_uri.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "ndef_record.h" #include diff --git a/esphome/components/nfc/nfc.h b/esphome/components/nfc/nfc.h index 23bfdd8ef0..2e5c5cd9c5 100644 --- a/esphome/components/nfc/nfc.h +++ b/esphome/components/nfc/nfc.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "ndef_record.h" #include "ndef_message.h" #include "nfc_tag.h" diff --git a/esphome/components/nfc/nfc_tag.h b/esphome/components/nfc/nfc_tag.h index 58875a744d..55600c3bd9 100644 --- a/esphome/components/nfc/nfc_tag.h +++ b/esphome/components/nfc/nfc_tag.h @@ -3,8 +3,8 @@ #include #include -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "ndef_message.h" namespace esphome { diff --git a/esphome/components/noblex/noblex.cpp b/esphome/components/noblex/noblex.cpp index 3521745bdc..53f807809e 100644 --- a/esphome/components/noblex/noblex.cpp +++ b/esphome/components/noblex/noblex.cpp @@ -1,6 +1,6 @@ #include "noblex.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace noblex { diff --git a/esphome/components/npi19/npi19.cpp b/esphome/components/npi19/npi19.cpp index ca1fc39943..17ca0ef23e 100644 --- a/esphome/components/npi19/npi19.cpp +++ b/esphome/components/npi19/npi19.cpp @@ -1,7 +1,7 @@ #include "npi19.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace npi19 { @@ -11,18 +11,16 @@ static const char *const TAG = "npi19"; static const uint8_t READ_COMMAND = 0xAC; void NPI19Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up NPI19..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint16_t raw_temperature(0); uint16_t raw_pressure(0); i2c::ErrorCode err = this->read_(raw_temperature, raw_pressure); if (err != i2c::ERROR_OK) { - ESP_LOGCONFIG(TAG, " I2C Communication Failed..."); + ESP_LOGCONFIG(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); return; } - - ESP_LOGCONFIG(TAG, " Success..."); } void NPI19Component::dump_config() { @@ -90,7 +88,7 @@ void NPI19Component::update() { i2c::ErrorCode err = this->read_(raw_temperature, raw_pressure); if (err != i2c::ERROR_OK) { - ESP_LOGW(TAG, "I2C Communication Failed"); + ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning(); return; } diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index 7aa103e9d9..2567d9ffe1 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -21,8 +21,10 @@ from esphome.const import ( CONF_WEB_SERVER, DEVICE_CLASS_APPARENT_POWER, DEVICE_CLASS_AQI, + DEVICE_CLASS_AREA, DEVICE_CLASS_ATMOSPHERIC_PRESSURE, DEVICE_CLASS_BATTERY, + DEVICE_CLASS_BLOOD_GLUCOSE_CONCENTRATION, DEVICE_CLASS_CARBON_DIOXIDE, DEVICE_CLASS_CARBON_MONOXIDE, DEVICE_CLASS_CONDUCTIVITY, @@ -33,6 +35,7 @@ from esphome.const import ( DEVICE_CLASS_DURATION, DEVICE_CLASS_EMPTY, DEVICE_CLASS_ENERGY, + DEVICE_CLASS_ENERGY_DISTANCE, DEVICE_CLASS_ENERGY_STORAGE, DEVICE_CLASS_FREQUENCY, DEVICE_CLASS_GAS, @@ -54,6 +57,7 @@ from esphome.const import ( DEVICE_CLASS_PRECIPITATION, DEVICE_CLASS_PRECIPITATION_INTENSITY, DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_REACTIVE_ENERGY, DEVICE_CLASS_REACTIVE_POWER, DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_SOUND_PRESSURE, @@ -68,6 +72,7 @@ from esphome.const import ( DEVICE_CLASS_VOLUME_STORAGE, DEVICE_CLASS_WATER, DEVICE_CLASS_WEIGHT, + DEVICE_CLASS_WIND_DIRECTION, DEVICE_CLASS_WIND_SPEED, ) from esphome.core import CORE, coroutine_with_priority @@ -78,8 +83,10 @@ CODEOWNERS = ["@esphome/core"] DEVICE_CLASSES = [ DEVICE_CLASS_APPARENT_POWER, DEVICE_CLASS_AQI, + DEVICE_CLASS_AREA, DEVICE_CLASS_ATMOSPHERIC_PRESSURE, DEVICE_CLASS_BATTERY, + DEVICE_CLASS_BLOOD_GLUCOSE_CONCENTRATION, DEVICE_CLASS_CARBON_DIOXIDE, DEVICE_CLASS_CARBON_MONOXIDE, DEVICE_CLASS_CONDUCTIVITY, @@ -90,6 +97,7 @@ DEVICE_CLASSES = [ DEVICE_CLASS_DURATION, DEVICE_CLASS_EMPTY, DEVICE_CLASS_ENERGY, + DEVICE_CLASS_ENERGY_DISTANCE, DEVICE_CLASS_ENERGY_STORAGE, DEVICE_CLASS_FREQUENCY, DEVICE_CLASS_GAS, @@ -111,6 +119,7 @@ DEVICE_CLASSES = [ DEVICE_CLASS_PRECIPITATION, DEVICE_CLASS_PRECIPITATION_INTENSITY, DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_REACTIVE_ENERGY, DEVICE_CLASS_REACTIVE_POWER, DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_SOUND_PRESSURE, @@ -125,6 +134,7 @@ DEVICE_CLASSES = [ DEVICE_CLASS_VOLUME_STORAGE, DEVICE_CLASS_WATER, DEVICE_CLASS_WEIGHT, + DEVICE_CLASS_WIND_DIRECTION, DEVICE_CLASS_WIND_SPEED, ] IS_PLATFORM_COMPONENT = True @@ -267,6 +277,7 @@ async def register_number( if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_number(var)) + CORE.register_platform_component("number", var) await setup_number_core_( var, config, min_value=min_value, max_value=max_value, step=step ) diff --git a/esphome/components/number/number.cpp b/esphome/components/number/number.cpp index fda4f43e34..b6a845b19b 100644 --- a/esphome/components/number/number.cpp +++ b/esphome/components/number/number.cpp @@ -7,7 +7,7 @@ namespace number { static const char *const TAG = "number"; void Number::publish_state(float state) { - this->has_state_ = true; + this->set_has_state(true); this->state = state; ESP_LOGD(TAG, "'%s': Sending state %f", this->get_name().c_str(), state); this->state_callback_.call(state); diff --git a/esphome/components/number/number.h b/esphome/components/number/number.h index d839d12ad1..49bcbb857c 100644 --- a/esphome/components/number/number.h +++ b/esphome/components/number/number.h @@ -48,9 +48,6 @@ class Number : public EntityBase { NumberTraits traits; - /// Return whether this number has gotten a full state yet. - bool has_state() const { return has_state_; } - protected: friend class NumberCall; @@ -63,7 +60,6 @@ class Number : public EntityBase { virtual void control(float value) = 0; CallbackManager state_callback_; - bool has_state_{false}; }; } // namespace number diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py index 55b9037176..9380cf1b1b 100644 --- a/esphome/components/online_image/__init__.py +++ b/esphome/components/online_image/__init__.py @@ -2,6 +2,7 @@ import logging from esphome import automation import esphome.codegen as cg +from esphome.components.const import CONF_REQUEST_HEADERS from esphome.components.http_request import CONF_HTTP_REQUEST_ID, HttpRequestComponent from esphome.components.image import ( CONF_INVERT_ALPHA, @@ -24,6 +25,7 @@ from esphome.const import ( CONF_TYPE, CONF_URL, ) +from esphome.core import Lambda AUTO_LOAD = ["image"] DEPENDENCIES = ["display", "http_request"] @@ -124,6 +126,9 @@ ONLINE_IMAGE_SCHEMA = ( cv.GenerateID(CONF_HTTP_REQUEST_ID): cv.use_id(HttpRequestComponent), # Online Image specific options cv.Required(CONF_URL): cv.url, + cv.Optional(CONF_REQUEST_HEADERS): cv.All( + cv.Schema({cv.string: cv.templatable(cv.string)}) + ), cv.Required(CONF_FORMAT): cv.one_of(*IMAGE_FORMATS, upper=True), cv.Optional(CONF_PLACEHOLDER): cv.use_id(Image_), cv.Optional(CONF_BUFFER_SIZE, default=65536): cv.int_range(256, 65536), @@ -207,13 +212,20 @@ async def to_code(config): await cg.register_component(var, config) await cg.register_parented(var, config[CONF_HTTP_REQUEST_ID]) + for key, value in config.get(CONF_REQUEST_HEADERS, {}).items(): + if isinstance(value, Lambda): + template_ = await cg.templatable(value, [], cg.std_string) + cg.add(var.add_request_header(key, template_)) + else: + cg.add(var.add_request_header(key, value)) + if placeholder_id := config.get(CONF_PLACEHOLDER): placeholder = await cg.get_variable(placeholder_id) cg.add(var.set_placeholder(placeholder)) for conf in config.get(CONF_ON_DOWNLOAD_FINISHED, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [], conf) + await automation.build_automation(trigger, [(bool, "cached")], conf) for conf in config.get(CONF_ON_ERROR, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index cb4a3be9e8..8030bd0095 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -3,6 +3,10 @@ #include "esphome/core/log.h" static const char *const TAG = "online_image"; +static const char *const ETAG_HEADER_NAME = "etag"; +static const char *const IF_NONE_MATCH_HEADER_NAME = "if-none-match"; +static const char *const LAST_MODIFIED_HEADER_NAME = "last-modified"; +static const char *const IF_MODIFIED_SINCE_HEADER_NAME = "if-modified-since"; #include "image_decoder.h" @@ -52,7 +56,7 @@ void OnlineImage::draw(int x, int y, display::Display *display, Color color_on, void OnlineImage::release() { if (this->buffer_) { - ESP_LOGV(TAG, "Deallocating old buffer..."); + ESP_LOGV(TAG, "Deallocating old buffer"); this->allocator_.deallocate(this->buffer_, this->get_buffer_size_()); this->data_start_ = nullptr; this->buffer_ = nullptr; @@ -60,6 +64,8 @@ void OnlineImage::release() { this->height_ = 0; this->buffer_width_ = 0; this->buffer_height_ = 0; + this->last_modified_ = ""; + this->etag_ = ""; this->end_connection_(); } } @@ -127,9 +133,21 @@ void OnlineImage::update() { } accept_header.value = accept_mime_type + ",*/*;q=0.8"; + if (!this->etag_.empty()) { + headers.push_back(http_request::Header{IF_NONE_MATCH_HEADER_NAME, this->etag_}); + } + + if (!this->last_modified_.empty()) { + headers.push_back(http_request::Header{IF_MODIFIED_SINCE_HEADER_NAME, this->last_modified_}); + } + headers.push_back(accept_header); - this->downloader_ = this->parent_->get(this->url_, headers); + for (auto &header : this->request_headers_) { + headers.push_back(http_request::Header{header.first, header.second.value()}); + } + + this->downloader_ = this->parent_->get(this->url_, headers, {ETAG_HEADER_NAME, LAST_MODIFIED_HEADER_NAME}); if (this->downloader_ == nullptr) { ESP_LOGE(TAG, "Download failed."); @@ -141,7 +159,9 @@ void OnlineImage::update() { int http_code = this->downloader_->status_code; if (http_code == HTTP_CODE_NOT_MODIFIED) { // Image hasn't changed on server. Skip download. + ESP_LOGI(TAG, "Server returned HTTP 304 (Not Modified). Download skipped."); this->end_connection_(); + this->download_finished_callback_.call(true); return; } if (http_code != HTTP_CODE_OK) { @@ -201,8 +221,10 @@ void OnlineImage::loop() { ESP_LOGD(TAG, "Image fully downloaded, read %zu bytes, width/height = %d/%d", this->downloader_->get_bytes_read(), this->width_, this->height_); ESP_LOGD(TAG, "Total time: %lds", ::time(nullptr) - this->start_time_); + this->etag_ = this->downloader_->get_response_header(ETAG_HEADER_NAME); + this->last_modified_ = this->downloader_->get_response_header(LAST_MODIFIED_HEADER_NAME); + this->download_finished_callback_.call(false); this->end_connection_(); - this->download_finished_callback_.call(); return; } if (this->downloader_ == nullptr) { @@ -325,7 +347,7 @@ bool OnlineImage::validate_url_(const std::string &url) { return true; } -void OnlineImage::add_on_finished_callback(std::function &&callback) { +void OnlineImage::add_on_finished_callback(std::function &&callback) { this->download_finished_callback_.add(std::move(callback)); } diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index 2d10e528b1..6ed9c7956f 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -63,6 +63,13 @@ class OnlineImage : public PollingComponent, if (this->validate_url_(url)) { this->url_ = url; } + this->etag_ = ""; + this->last_modified_ = ""; + } + + /** Add the request header */ + template void add_request_header(const std::string &header, V value) { + this->request_headers_.push_back(std::pair >(header, value)); } /** @@ -86,7 +93,7 @@ class OnlineImage : public PollingComponent, */ size_t resize_download_buffer(size_t size) { return this->download_buffer_.resize(size); } - void add_on_finished_callback(std::function &&callback); + void add_on_finished_callback(std::function &&callback); void add_on_error_callback(std::function &&callback); protected: @@ -131,7 +138,7 @@ class OnlineImage : public PollingComponent, void end_connection_(); - CallbackManager download_finished_callback_{}; + CallbackManager download_finished_callback_{}; CallbackManager download_error_callback_{}; std::shared_ptr downloader_{nullptr}; @@ -151,6 +158,8 @@ class OnlineImage : public PollingComponent, std::string url_{""}; + std::vector > > request_headers_; + /** width requested on configuration, or 0 if non specified. */ const int fixed_width_; /** height requested on configuration, or 0 if non specified. */ @@ -173,6 +182,14 @@ class OnlineImage : public PollingComponent, * decoded images). */ int buffer_height_; + /** + * The value of the ETag HTTP header provided in the last response. + */ + std::string etag_ = ""; + /** + * The value of the Last-Modified HTTP header provided in the last response. + */ + std::string last_modified_ = ""; time_t start_time_; @@ -202,10 +219,10 @@ template class OnlineImageReleaseAction : public Action { OnlineImage *parent_; }; -class DownloadFinishedTrigger : public Trigger<> { +class DownloadFinishedTrigger : public Trigger { public: explicit DownloadFinishedTrigger(OnlineImage *parent) { - parent->add_on_finished_callback([this]() { this->trigger(); }); + parent->add_on_finished_callback([this](bool cached) { this->trigger(cached); }); } }; diff --git a/esphome/components/opentherm/generate.py b/esphome/components/opentherm/generate.py index a97754d52c..4e6f3b0a12 100644 --- a/esphome/components/opentherm/generate.py +++ b/esphome/components/opentherm/generate.py @@ -1,5 +1,5 @@ -from collections.abc import Awaitable -from typing import Any, Callable, Optional +from collections.abc import Awaitable, Callable +from typing import Any import esphome.codegen as cg from esphome.const import CONF_ID @@ -103,7 +103,7 @@ def define_setting_readers(component_type: str, keys: list[str]) -> None: def add_messages(hub: cg.MockObj, keys: list[str], schemas: dict[str, TSchema]): - messages: dict[str, tuple[bool, Optional[int]]] = {} + messages: dict[str, tuple[bool, int | None]] = {} for key in keys: messages[schemas[key].message] = ( schemas[key].keep_updated, diff --git a/esphome/components/opentherm/hub.cpp b/esphome/components/opentherm/hub.cpp index 97adf71752..0a4ef98507 100644 --- a/esphome/components/opentherm/hub.cpp +++ b/esphome/components/opentherm/hub.cpp @@ -399,13 +399,17 @@ void OpenthermHub::dump_config() { ESP_LOGCONFIG(TAG, "OpenTherm:"); LOG_PIN(" In: ", this->in_pin_); LOG_PIN(" Out: ", this->out_pin_); - ESP_LOGCONFIG(TAG, " Sync mode: %s", YESNO(this->sync_mode_)); - ESP_LOGCONFIG(TAG, " Sensors: %s", SHOW(OPENTHERM_SENSOR_LIST(ID, ))); - ESP_LOGCONFIG(TAG, " Binary sensors: %s", SHOW(OPENTHERM_BINARY_SENSOR_LIST(ID, ))); - ESP_LOGCONFIG(TAG, " Switches: %s", SHOW(OPENTHERM_SWITCH_LIST(ID, ))); - ESP_LOGCONFIG(TAG, " Input sensors: %s", SHOW(OPENTHERM_INPUT_SENSOR_LIST(ID, ))); - ESP_LOGCONFIG(TAG, " Outputs: %s", SHOW(OPENTHERM_OUTPUT_LIST(ID, ))); - ESP_LOGCONFIG(TAG, " Numbers: %s", SHOW(OPENTHERM_NUMBER_LIST(ID, ))); + ESP_LOGCONFIG(TAG, + " Sync mode: %s\n" + " Sensors: %s\n" + " Binary sensors: %s\n" + " Switches: %s\n" + " Input sensors: %s\n" + " Outputs: %s\n" + " Numbers: %s", + YESNO(this->sync_mode_), SHOW(OPENTHERM_SENSOR_LIST(ID, )), SHOW(OPENTHERM_BINARY_SENSOR_LIST(ID, )), + SHOW(OPENTHERM_SWITCH_LIST(ID, )), SHOW(OPENTHERM_INPUT_SENSOR_LIST(ID, )), + SHOW(OPENTHERM_OUTPUT_LIST(ID, )), SHOW(OPENTHERM_NUMBER_LIST(ID, ))); ESP_LOGCONFIG(TAG, " Initial requests:"); for (auto type : initial_messages) { ESP_LOGCONFIG(TAG, " - %d (%s)", type, this->opentherm_->message_id_to_str(type)); diff --git a/esphome/components/opentherm/number/number.cpp b/esphome/components/opentherm/number/number.cpp index d02b99ee9c..90ab5d6490 100644 --- a/esphome/components/opentherm/number/number.cpp +++ b/esphome/components/opentherm/number/number.cpp @@ -31,9 +31,11 @@ void OpenthermNumber::setup() { void OpenthermNumber::dump_config() { LOG_NUMBER("", "OpenTherm Number", this); - ESP_LOGCONFIG(TAG, " Restore value: %d", this->restore_value_); - ESP_LOGCONFIG(TAG, " Initial value: %.2f", this->initial_value_); - ESP_LOGCONFIG(TAG, " Current value: %.2f", this->state); + ESP_LOGCONFIG(TAG, + " Restore value: %d\n" + " Initial value: %.2f\n" + " Current value: %.2f", + this->restore_value_, this->initial_value_, this->state); } } // namespace opentherm diff --git a/esphome/components/opentherm/opentherm.h b/esphome/components/opentherm/opentherm.h index 4280832d09..a5822cdfe1 100644 --- a/esphome/components/opentherm/opentherm.h +++ b/esphome/components/opentherm/opentherm.h @@ -9,8 +9,8 @@ #include #include "esphome/core/hal.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #if defined(ESP32) || defined(USE_ESP_IDF) #include "driver/timer.h" diff --git a/esphome/components/opentherm/schema.py b/esphome/components/opentherm/schema.py index 791ba215e0..f70c8e24db 100644 --- a/esphome/components/opentherm/schema.py +++ b/esphome/components/opentherm/schema.py @@ -2,7 +2,7 @@ # inputs of the OpenTherm component. from dataclasses import dataclass -from typing import Any, Optional, TypeVar +from typing import Any, TypeVar import esphome.config_validation as cv from esphome.const import ( @@ -61,11 +61,11 @@ TSchema = TypeVar("TSchema", bound=EntitySchema) class SensorSchema(EntitySchema): accuracy_decimals: int state_class: str - unit_of_measurement: Optional[str] = None - icon: Optional[str] = None - device_class: Optional[str] = None + unit_of_measurement: str | None = None + icon: str | None = None + device_class: str | None = None disabled_by_default: bool = False - order: Optional[int] = None + order: int | None = None SENSORS: dict[str, SensorSchema] = { @@ -461,9 +461,9 @@ SENSORS: dict[str, SensorSchema] = { @dataclass class BinarySensorSchema(EntitySchema): - icon: Optional[str] = None - device_class: Optional[str] = None - order: Optional[int] = None + icon: str | None = None + device_class: str | None = None + order: int | None = None BINARY_SENSORS: dict[str, BinarySensorSchema] = { @@ -654,7 +654,7 @@ BINARY_SENSORS: dict[str, BinarySensorSchema] = { @dataclass class SwitchSchema(EntitySchema): - default_mode: Optional[str] = None + default_mode: str | None = None SWITCHES: dict[str, SwitchSchema] = { @@ -721,9 +721,9 @@ class InputSchema(EntitySchema): unit_of_measurement: str step: float range: tuple[int, int] - icon: Optional[str] = None - auto_max_value: Optional[AutoConfigure] = None - auto_min_value: Optional[AutoConfigure] = None + icon: str | None = None + auto_max_value: AutoConfigure | None = None + auto_min_value: AutoConfigure | None = None INPUTS: dict[str, InputSchema] = { @@ -834,7 +834,7 @@ class SettingSchema(EntitySchema): backing_type: str validation_schema: cv.Schema default_value: Any - order: Optional[int] = None + order: int | None = None SETTINGS: dict[str, SettingSchema] = { diff --git a/esphome/components/opentherm/validate.py b/esphome/components/opentherm/validate.py index 2b80e59f7b..998bcde57f 100644 --- a/esphome/components/opentherm/validate.py +++ b/esphome/components/opentherm/validate.py @@ -1,4 +1,4 @@ -from typing import Callable +from collections.abc import Callable from voluptuous import Schema diff --git a/esphome/components/openthread/__init__.py b/esphome/components/openthread/__init__.py new file mode 100644 index 0000000000..5b1ea491e3 --- /dev/null +++ b/esphome/components/openthread/__init__.py @@ -0,0 +1,146 @@ +import esphome.codegen as cg +from esphome.components.esp32 import ( + VARIANT_ESP32C6, + VARIANT_ESP32H2, + add_idf_sdkconfig_option, + only_on_variant, +) +from esphome.components.mdns import MDNSComponent +import esphome.config_validation as cv +from esphome.const import CONF_CHANNEL, CONF_ENABLE_IPV6, CONF_ID +import esphome.final_validate as fv + +from .const import ( + CONF_EXT_PAN_ID, + CONF_FORCE_DATASET, + CONF_MDNS_ID, + CONF_MESH_LOCAL_PREFIX, + CONF_NETWORK_KEY, + CONF_NETWORK_NAME, + CONF_PAN_ID, + CONF_PSKC, + CONF_SRP_ID, + CONF_TLV, +) +from .tlv import parse_tlv + +CODEOWNERS = ["@mrene"] + +AUTO_LOAD = ["network"] + +# Wi-fi / Bluetooth / Thread coexistence isn't implemented at this time +# TODO: Doesn't conflict with wifi if you're using another ESP as an RCP (radio coprocessor), but this isn't implemented yet +CONFLICTS_WITH = ["wifi"] +DEPENDENCIES = ["esp32"] + + +def set_sdkconfig_options(config): + # and expose options for using SPI/UART RCPs + add_idf_sdkconfig_option("CONFIG_IEEE802154_ENABLED", True) + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_RADIO_NATIVE", True) + + # There is a conflict if the logger's uart also uses the default UART, which is seen as a watchdog failure on "ot_cli" + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_CLI", False) + + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_ENABLED", True) + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PANID", config[CONF_PAN_ID]) + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_CHANNEL", config[CONF_CHANNEL]) + add_idf_sdkconfig_option( + "CONFIG_OPENTHREAD_NETWORK_MASTERKEY", f"{config[CONF_NETWORK_KEY]:X}" + ) + + if network_name := config.get(CONF_NETWORK_NAME): + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_NAME", network_name) + + if (ext_pan_id := config.get(CONF_EXT_PAN_ID)) is not None: + add_idf_sdkconfig_option( + "CONFIG_OPENTHREAD_NETWORK_EXTPANID", f"{ext_pan_id:X}" + ) + if (mesh_local_prefix := config.get(CONF_MESH_LOCAL_PREFIX)) is not None: + add_idf_sdkconfig_option( + "CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX", f"{mesh_local_prefix:X}" + ) + if (pskc := config.get(CONF_PSKC)) is not None: + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PSKC", f"{pskc:X}") + + if CONF_FORCE_DATASET in config: + if config[CONF_FORCE_DATASET]: + cg.add_define("CONFIG_OPENTHREAD_FORCE_DATASET") + + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_DNS64_CLIENT", True) + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_SRP_CLIENT", True) + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_SRP_CLIENT_MAX_SERVICES", 5) + + # TODO: Add suport for sleepy end devices + add_idf_sdkconfig_option("CONFIG_OPENTHREAD_FTD", True) # Full Thread Device + + +openthread_ns = cg.esphome_ns.namespace("openthread") +OpenThreadComponent = openthread_ns.class_("OpenThreadComponent", cg.Component) +OpenThreadSrpComponent = openthread_ns.class_("OpenThreadSrpComponent", cg.Component) + + +def _convert_tlv(config): + if tlv := config.get(CONF_TLV): + config = config.copy() + parsed_tlv = parse_tlv(tlv) + validated = _CONNECTION_SCHEMA(parsed_tlv) + config.update(validated) + del config[CONF_TLV] + return config + + +_CONNECTION_SCHEMA = cv.Schema( + { + cv.Inclusive(CONF_PAN_ID, "manual"): cv.hex_int, + cv.Inclusive(CONF_CHANNEL, "manual"): cv.int_, + cv.Inclusive(CONF_NETWORK_KEY, "manual"): cv.hex_int, + cv.Optional(CONF_EXT_PAN_ID): cv.hex_int, + cv.Optional(CONF_NETWORK_NAME): cv.string_strict, + cv.Optional(CONF_PSKC): cv.hex_int, + cv.Optional(CONF_MESH_LOCAL_PREFIX): cv.hex_int, + } +) + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(OpenThreadComponent), + cv.GenerateID(CONF_SRP_ID): cv.declare_id(OpenThreadSrpComponent), + cv.GenerateID(CONF_MDNS_ID): cv.use_id(MDNSComponent), + cv.Optional(CONF_FORCE_DATASET): cv.boolean, + cv.Optional(CONF_TLV): cv.string_strict, + } + ).extend(_CONNECTION_SCHEMA), + cv.has_exactly_one_key(CONF_PAN_ID, CONF_TLV), + _convert_tlv, + cv.only_with_esp_idf, + only_on_variant(supported=[VARIANT_ESP32C6, VARIANT_ESP32H2]), +) + + +def _final_validate(_): + full_config = fv.full_config.get() + network_config = full_config.get("network", {}) + if not network_config.get(CONF_ENABLE_IPV6, False): + raise cv.Invalid( + "OpenThread requires IPv6 to be enabled in the network component. " + "Please set `enable_ipv6: true` in the `network` configuration." + ) + + +FINAL_VALIDATE_SCHEMA = _final_validate + + +async def to_code(config): + cg.add_define("USE_OPENTHREAD") + + ot = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(ot, config) + + srp = cg.new_Pvariable(config[CONF_SRP_ID]) + mdns_component = await cg.get_variable(config[CONF_MDNS_ID]) + cg.add(srp.set_mdns(mdns_component)) + await cg.register_component(srp, config) + + set_sdkconfig_options(config) diff --git a/esphome/components/openthread/const.py b/esphome/components/openthread/const.py new file mode 100644 index 0000000000..7837e69eea --- /dev/null +++ b/esphome/components/openthread/const.py @@ -0,0 +1,10 @@ +CONF_EXT_PAN_ID = "ext_pan_id" +CONF_FORCE_DATASET = "force_dataset" +CONF_MDNS_ID = "mdns_id" +CONF_MESH_LOCAL_PREFIX = "mesh_local_prefix" +CONF_NETWORK_NAME = "network_name" +CONF_NETWORK_KEY = "network_key" +CONF_PAN_ID = "pan_id" +CONF_PSKC = "pskc" +CONF_SRP_ID = "srp_id" +CONF_TLV = "tlv" diff --git a/esphome/components/openthread/openthread.cpp b/esphome/components/openthread/openthread.cpp new file mode 100644 index 0000000000..f40a56952a --- /dev/null +++ b/esphome/components/openthread/openthread.cpp @@ -0,0 +1,206 @@ +#include "esphome/core/defines.h" +#ifdef USE_OPENTHREAD +#include "openthread.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +static const char *const TAG = "openthread"; + +namespace esphome { +namespace openthread { + +OpenThreadComponent *global_openthread_component = // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +OpenThreadComponent::OpenThreadComponent() { global_openthread_component = this; } + +OpenThreadComponent::~OpenThreadComponent() { + auto lock = InstanceLock::try_acquire(100); + if (!lock) { + ESP_LOGW(TAG, "Failed to acquire OpenThread lock in destructor, leaking memory"); + return; + } + otInstance *instance = lock->get_instance(); + otSrpClientClearHostAndServices(instance); + otSrpClientBuffersFreeAllServices(instance); + global_openthread_component = nullptr; +} + +bool OpenThreadComponent::is_connected() { + auto lock = InstanceLock::try_acquire(100); + if (!lock) { + ESP_LOGW(TAG, "Failed to acquire OpenThread lock in is_connected"); + return false; + } + + otInstance *instance = lock->get_instance(); + if (instance == nullptr) { + return false; + } + + otDeviceRole role = otThreadGetDeviceRole(instance); + + // TODO: If we're a leader, check that there is at least 1 known peer + return role >= OT_DEVICE_ROLE_CHILD; +} + +// Gets the off-mesh routable address +std::optional OpenThreadComponent::get_omr_address() { + InstanceLock lock = InstanceLock::acquire(); + return this->get_omr_address_(lock); +} + +std::optional OpenThreadComponent::get_omr_address_(InstanceLock &lock) { + otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; + otInstance *instance = nullptr; + + instance = lock.get_instance(); + + otBorderRouterConfig config; + if (otNetDataGetNextOnMeshPrefix(instance, &iterator, &config) != OT_ERROR_NONE) { + return std::nullopt; + } + + const otIp6Prefix *omr_prefix = &config.mPrefix; + const otNetifAddress *unicast_addresses = otIp6GetUnicastAddresses(instance); + for (const otNetifAddress *addr = unicast_addresses; addr; addr = addr->mNext) { + const otIp6Address *local_ip = &addr->mAddress; + if (otIp6PrefixMatch(&omr_prefix->mPrefix, local_ip)) { + return *local_ip; + } + } + return {}; +} + +void srp_callback(otError err, const otSrpClientHostInfo *host_info, const otSrpClientService *services, + const otSrpClientService *removed_services, void *context) { + if (err != 0) { + ESP_LOGW(TAG, "SRP client reported an error: %s", otThreadErrorToString(err)); + for (const otSrpClientHostInfo *host = host_info; host; host = nullptr) { + ESP_LOGW(TAG, " Host: %s", host->mName); + } + for (const otSrpClientService *service = services; service; service = service->mNext) { + ESP_LOGW(TAG, " Service: %s", service->mName); + } + } +} + +void srp_start_callback(const otSockAddr *server_socket_address, void *context) { + ESP_LOGI(TAG, "SRP client has started"); +} + +void OpenThreadSrpComponent::setup() { + otError error; + InstanceLock lock = InstanceLock::acquire(); + otInstance *instance = lock.get_instance(); + + otSrpClientSetCallback(instance, srp_callback, nullptr); + + // set the host name + uint16_t size; + char *existing_host_name = otSrpClientBuffersGetHostNameString(instance, &size); + const std::string &host_name = App.get_name(); + uint16_t host_name_len = host_name.size(); + if (host_name_len > size) { + ESP_LOGW(TAG, "Hostname is too long, choose a shorter project name"); + return; + } + memset(existing_host_name, 0, size); + memcpy(existing_host_name, host_name.c_str(), host_name_len); + + error = otSrpClientSetHostName(instance, existing_host_name); + if (error != 0) { + ESP_LOGW(TAG, "Could not set host name"); + return; + } + + error = otSrpClientEnableAutoHostAddress(instance); + if (error != 0) { + ESP_LOGW(TAG, "Could not enable auto host address"); + return; + } + + // Copy the mdns services to our local instance so that the c_str pointers remain valid for the lifetime of this + // component + this->mdns_services_ = this->mdns_->get_services(); + ESP_LOGW(TAG, "Setting up SRP services. count = %d\n", this->mdns_services_.size()); + for (const auto &service : this->mdns_services_) { + otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance); + if (!entry) { + ESP_LOGW(TAG, "Failed to allocate service entry"); + continue; + } + + // Set service name + char *string = otSrpClientBuffersGetServiceEntryServiceNameString(entry, &size); + std::string full_service = service.service_type + "." + service.proto; + if (full_service.size() > size) { + ESP_LOGW(TAG, "Service name too long: %s", full_service.c_str()); + continue; + } + memcpy(string, full_service.c_str(), full_service.size() + 1); + + // Set instance name (using host_name) + string = otSrpClientBuffersGetServiceEntryInstanceNameString(entry, &size); + if (host_name_len > size) { + ESP_LOGW(TAG, "Instance name too long: %s", host_name.c_str()); + continue; + } + memset(string, 0, size); + memcpy(string, host_name.c_str(), host_name_len); + + // Set port + entry->mService.mPort = const_cast &>(service.port).value(); + + otDnsTxtEntry *txt_entries = + reinterpret_cast(this->pool_alloc_(sizeof(otDnsTxtEntry) * service.txt_records.size())); + // Set TXT records + entry->mService.mNumTxtEntries = service.txt_records.size(); + for (size_t i = 0; i < service.txt_records.size(); i++) { + const auto &txt = service.txt_records[i]; + auto value = const_cast &>(txt.value).value(); + txt_entries[i].mKey = strdup(txt.key.c_str()); + txt_entries[i].mValue = reinterpret_cast(strdup(value.c_str())); + txt_entries[i].mValueLength = value.size(); + } + entry->mService.mTxtEntries = txt_entries; + entry->mService.mNumTxtEntries = service.txt_records.size(); + + // Add service + error = otSrpClientAddService(instance, &entry->mService); + if (error != OT_ERROR_NONE) { + ESP_LOGW(TAG, "Failed to add service: %s", otThreadErrorToString(error)); + } + ESP_LOGW(TAG, "Added service: %s", full_service.c_str()); + } + + otSrpClientEnableAutoStartMode(instance, srp_start_callback, nullptr); + ESP_LOGW(TAG, "Finished SRP setup"); +} + +void *OpenThreadSrpComponent::pool_alloc_(size_t size) { + uint8_t *ptr = new uint8_t[size]; + this->memory_pool_.emplace_back(std::unique_ptr(ptr)); + return ptr; +} + +void OpenThreadSrpComponent::set_mdns(esphome::mdns::MDNSComponent *mdns) { this->mdns_ = mdns; } + +} // namespace openthread +} // namespace esphome + +#endif diff --git a/esphome/components/openthread/openthread.h b/esphome/components/openthread/openthread.h new file mode 100644 index 0000000000..77fd58851a --- /dev/null +++ b/esphome/components/openthread/openthread.h @@ -0,0 +1,68 @@ +#pragma once +#include "esphome/core/defines.h" +#ifdef USE_OPENTHREAD + +#include "esphome/components/mdns/mdns_component.h" +#include "esphome/components/network/ip_address.h" +#include "esphome/core/component.h" + +#include + +#include +#include + +namespace esphome { +namespace openthread { + +class InstanceLock; + +class OpenThreadComponent : public Component { + public: + OpenThreadComponent(); + ~OpenThreadComponent(); + void setup() override; + float get_setup_priority() const override { return setup_priority::WIFI; } + + bool is_connected(); + network::IPAddresses get_ip_addresses(); + std::optional get_omr_address(); + void ot_main(); + + protected: + std::optional get_omr_address_(InstanceLock &lock); +}; + +extern OpenThreadComponent *global_openthread_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +class OpenThreadSrpComponent : public Component { + public: + void set_mdns(esphome::mdns::MDNSComponent *mdns); + // This has to run after the mdns component or else no services are available to advertise + float get_setup_priority() const override { return this->mdns_->get_setup_priority() - 1.0; } + void setup() override; + + protected: + esphome::mdns::MDNSComponent *mdns_{nullptr}; + std::vector mdns_services_; + std::vector> memory_pool_; + void *pool_alloc_(size_t size); +}; + +class InstanceLock { + public: + static std::optional try_acquire(int delay); + static InstanceLock acquire(); + ~InstanceLock(); + + // Returns the global openthread instance guarded by this lock + otInstance *get_instance(); + + private: + // Use a private constructor in order to force thehandling + // of acquisition failure + InstanceLock() {} +}; + +} // namespace openthread +} // namespace esphome +#endif diff --git a/esphome/components/openthread/openthread_esp.cpp b/esphome/components/openthread/openthread_esp.cpp new file mode 100644 index 0000000000..c5c817382f --- /dev/null +++ b/esphome/components/openthread/openthread_esp.cpp @@ -0,0 +1,164 @@ +#include "esphome/core/defines.h" +#if defined(USE_OPENTHREAD) && defined(USE_ESP_IDF) +#include +#include "openthread.h" + +#include "esp_log.h" +#include "esp_openthread.h" +#include "esp_openthread_lock.h" + +#include "esp_task_wdt.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#include "esp_err.h" +#include "esp_event.h" +#include "esp_netif.h" +#include "esp_netif_types.h" +#include "esp_openthread_cli.h" +#include "esp_openthread_netif_glue.h" +#include "esp_vfs_eventfd.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "nvs_flash.h" + +static const char *const TAG = "openthread"; + +namespace esphome { +namespace openthread { + +void OpenThreadComponent::setup() { + ESP_LOGCONFIG(TAG, "Running setup"); + // Used eventfds: + // * netif + // * ot task queue + // * radio driver + esp_vfs_eventfd_config_t eventfd_config = { + .max_fds = 3, + }; + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); + + xTaskCreate( + [](void *arg) { + static_cast(arg)->ot_main(); + vTaskDelete(nullptr); + }, + "ot_main", 10240, this, 5, nullptr); +} + +static esp_netif_t *init_openthread_netif(const esp_openthread_platform_config_t *config) { + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); + esp_netif_t *netif = esp_netif_new(&cfg); + assert(netif != nullptr); + ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config))); + + return netif; +} + +void OpenThreadComponent::ot_main() { + esp_openthread_platform_config_t config = { + .radio_config = + { + .radio_mode = RADIO_MODE_NATIVE, + .radio_uart_config = {}, + }, + .host_config = + { + // There is a conflict between esphome's logger which also + // claims the usb serial jtag device. + // .host_connection_mode = HOST_CONNECTION_MODE_CLI_USB, + // .host_usb_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(), + }, + .port_config = + { + .storage_partition_name = "nvs", + .netif_queue_size = 10, + .task_queue_size = 10, + }, + }; + + // Initialize the OpenThread stack + // otLoggingSetLevel(OT_LOG_LEVEL_DEBG); + ESP_ERROR_CHECK(esp_openthread_init(&config)); + +#if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE + ESP_ERROR_CHECK(esp_openthread_state_indicator_init(esp_openthread_get_instance())); +#endif + +#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC + // The OpenThread log level directly matches ESP log level + (void) otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); +#endif + // Initialize the OpenThread cli +#if CONFIG_OPENTHREAD_CLI + esp_openthread_cli_init(); +#endif + + esp_netif_t *openthread_netif; + // Initialize the esp_netif bindings + openthread_netif = init_openthread_netif(&config); + esp_netif_set_default_netif(openthread_netif); + +#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION + esp_cli_custom_command_init(); +#endif // CONFIG_OPENTHREAD_CLI_ESP_EXTENSION + + // Run the main loop +#if CONFIG_OPENTHREAD_CLI + esp_openthread_cli_create_task(); +#endif + ESP_LOGI(TAG, "Activating dataset..."); + otOperationalDatasetTlvs dataset; + +#ifdef CONFIG_OPENTHREAD_FORCE_DATASET + ESP_ERROR_CHECK(esp_openthread_auto_start(NULL)); +#else + otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset); + ESP_ERROR_CHECK(esp_openthread_auto_start((error == OT_ERROR_NONE) ? &dataset : NULL)); +#endif + esp_openthread_launch_mainloop(); + + // Clean up + esp_openthread_netif_glue_deinit(); + esp_netif_destroy(openthread_netif); + + esp_vfs_eventfd_unregister(); +} + +network::IPAddresses OpenThreadComponent::get_ip_addresses() { + network::IPAddresses addresses; + struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES]; + uint8_t count = 0; + esp_netif_t *netif = esp_netif_get_default_netif(); + count = esp_netif_get_all_ip6(netif, if_ip6s); + assert(count <= CONFIG_LWIP_IPV6_NUM_ADDRESSES); + for (int i = 0; i < count; i++) { + addresses[i + 1] = network::IPAddress(&if_ip6s[i]); + } + return addresses; +} + +std::optional InstanceLock::try_acquire(int delay) { + if (esp_openthread_lock_acquire(delay)) { + return InstanceLock(); + } + return {}; +} + +InstanceLock InstanceLock::acquire() { + while (!esp_openthread_lock_acquire(100)) { + esp_task_wdt_reset(); + } + return InstanceLock(); +} + +otInstance *InstanceLock::get_instance() { return esp_openthread_get_instance(); } + +InstanceLock::~InstanceLock() { esp_openthread_lock_release(); } + +} // namespace openthread +} // namespace esphome +#endif diff --git a/esphome/components/openthread/tlv.py b/esphome/components/openthread/tlv.py new file mode 100644 index 0000000000..45c8c47227 --- /dev/null +++ b/esphome/components/openthread/tlv.py @@ -0,0 +1,58 @@ +# Sourced from https://gist.github.com/agners/0338576e0003318b63ec1ea75adc90f9 +import binascii + +from esphome.const import CONF_CHANNEL + +from . import ( + CONF_EXT_PAN_ID, + CONF_MESH_LOCAL_PREFIX, + CONF_NETWORK_KEY, + CONF_NETWORK_NAME, + CONF_PAN_ID, + CONF_PSKC, +) + +TLV_TYPES = { + 0: CONF_CHANNEL, + 1: CONF_PAN_ID, + 2: CONF_EXT_PAN_ID, + 3: CONF_NETWORK_NAME, + 4: CONF_PSKC, + 5: CONF_NETWORK_KEY, + 7: CONF_MESH_LOCAL_PREFIX, +} + + +def parse_tlv(tlv) -> dict: + data = binascii.a2b_hex(tlv) + output = {} + pos = 0 + while pos < len(data): + tag = data[pos] + pos += 1 + _len = data[pos] + pos += 1 + val = data[pos : pos + _len] + pos += _len + if tag in TLV_TYPES: + if tag == 3: + output[TLV_TYPES[tag]] = val.decode("utf-8") + else: + output[TLV_TYPES[tag]] = int.from_bytes(val) + return output + + +def main(): + import sys + + args = sys.argv[1:] + parsed = parse_tlv(args[0]) + # print the parsed TLV data + for key, value in parsed.items(): + if isinstance(value, bytes): + value = value.hex() + print(f"{key}: {value}") + + +if __name__ == "__main__": + main() diff --git a/esphome/components/openthread_info/__init__.py b/esphome/components/openthread_info/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/openthread_info/openthread_info_text_sensor.cpp b/esphome/components/openthread_info/openthread_info_text_sensor.cpp new file mode 100644 index 0000000000..10724f3e2f --- /dev/null +++ b/esphome/components/openthread_info/openthread_info_text_sensor.cpp @@ -0,0 +1,24 @@ + +#include "openthread_info_text_sensor.h" +#ifdef USE_OPENTHREAD +#include "esphome/core/log.h" + +namespace esphome { +namespace openthread_info { + +static const char *const TAG = "openthread_info"; + +void IPAddressOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "IPAddress", this); } +void RoleOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Role", this); } +void ChannelOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Channel", this); } +void Rloc16OpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Rloc16", this); } +void ExtAddrOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "ExtAddr", this); } +void Eui64OpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Eui64", this); } +void NetworkNameOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Network Name", this); } +void NetworkKeyOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Network Key", this); } +void PanIdOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "PAN ID", this); } +void ExtPanIdOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "Extended PAN ID", this); } + +} // namespace openthread_info +} // namespace esphome +#endif diff --git a/esphome/components/openthread_info/openthread_info_text_sensor.h b/esphome/components/openthread_info/openthread_info_text_sensor.h new file mode 100644 index 0000000000..bbcd2d4655 --- /dev/null +++ b/esphome/components/openthread_info/openthread_info_text_sensor.h @@ -0,0 +1,218 @@ +#pragma once + +#include "esphome/components/openthread/openthread.h" +#include "esphome/components/text_sensor/text_sensor.h" +#include "esphome/core/component.h" +#ifdef USE_OPENTHREAD + +namespace esphome { +namespace openthread_info { + +using esphome::openthread::InstanceLock; + +class OpenThreadInstancePollingComponent : public PollingComponent { + public: + void update() override { + auto lock = InstanceLock::try_acquire(10); + if (!lock) { + return; + } + + this->update_instance(lock->get_instance()); + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + + protected: + virtual void update_instance(otInstance *instance) = 0; +}; + +class IPAddressOpenThreadInfo : public PollingComponent, public text_sensor::TextSensor { + public: + void update() override { + std::optional address = openthread::global_openthread_component->get_omr_address(); + if (!address) { + return; + } + + char address_as_string[40]; + otIp6AddressToString(&*address, address_as_string, 40); + std::string ip = address_as_string; + + if (this->last_ip_ != ip) { + this->last_ip_ = ip; + this->publish_state(this->last_ip_); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + std::string last_ip_; +}; + +class RoleOpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor { + public: + void update_instance(otInstance *instance) override { + otDeviceRole role = otThreadGetDeviceRole(instance); + + if (this->last_role_ != role) { + this->last_role_ = role; + this->publish_state(otThreadDeviceRoleToString(this->last_role_)); + } + } + void dump_config() override; + + protected: + otDeviceRole last_role_; +}; + +class Rloc16OpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor { + public: + void update_instance(otInstance *instance) override { + uint16_t rloc16 = otThreadGetRloc16(instance); + if (this->last_rloc16_ != rloc16) { + this->last_rloc16_ = rloc16; + char buf[5]; + snprintf(buf, sizeof(buf), "%04x", rloc16); + this->publish_state(buf); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + uint16_t last_rloc16_; +}; + +class ExtAddrOpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor { + public: + void update_instance(otInstance *instance) override { + const auto *extaddr = otLinkGetExtendedAddress(instance); + if (!std::equal(this->last_extaddr_.begin(), this->last_extaddr_.end(), extaddr->m8)) { + std::copy(extaddr->m8, extaddr->m8 + 8, this->last_extaddr_.begin()); + this->publish_state(format_hex(extaddr->m8, 8)); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + std::array last_extaddr_{}; +}; + +class Eui64OpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor { + public: + void update_instance(otInstance *instance) override { + otExtAddress addr; + otLinkGetFactoryAssignedIeeeEui64(instance, &addr); + + if (!std::equal(this->last_eui64_.begin(), this->last_eui64_.end(), addr.m8)) { + std::copy(addr.m8, addr.m8 + 8, this->last_eui64_.begin()); + this->publish_state(format_hex(this->last_eui64_.begin(), 8)); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + std::array last_eui64_{}; +}; + +class ChannelOpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor { + public: + void update_instance(otInstance *instance) override { + uint8_t channel = otLinkGetChannel(instance); + if (this->last_channel_ != channel) { + this->last_channel_ = channel; + this->publish_state(std::to_string(this->last_channel_)); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + uint8_t last_channel_; +}; + +class DatasetOpenThreadInfo : public OpenThreadInstancePollingComponent { + public: + void update_instance(otInstance *instance) override { + otOperationalDataset dataset; + if (otDatasetGetActive(instance, &dataset) != OT_ERROR_NONE) { + return; + } + + this->update_dataset(&dataset); + } + + protected: + virtual void update_dataset(otOperationalDataset *dataset) = 0; +}; + +class NetworkNameOpenThreadInfo : public DatasetOpenThreadInfo, public text_sensor::TextSensor { + public: + void update_dataset(otOperationalDataset *dataset) override { + if (this->last_network_name_ != dataset->mNetworkName.m8) { + this->last_network_name_ = dataset->mNetworkName.m8; + this->publish_state(this->last_network_name_); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + std::string last_network_name_; +}; + +class NetworkKeyOpenThreadInfo : public DatasetOpenThreadInfo, public text_sensor::TextSensor { + public: + void update_dataset(otOperationalDataset *dataset) override { + if (!std::equal(this->last_key_.begin(), this->last_key_.end(), dataset->mNetworkKey.m8)) { + std::copy(dataset->mNetworkKey.m8, dataset->mNetworkKey.m8 + 16, this->last_key_.begin()); + this->publish_state(format_hex(dataset->mNetworkKey.m8, 16)); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + std::array last_key_{}; +}; + +class PanIdOpenThreadInfo : public DatasetOpenThreadInfo, public text_sensor::TextSensor { + public: + void update_dataset(otOperationalDataset *dataset) override { + uint16_t panid = dataset->mPanId; + if (this->last_panid_ != panid) { + this->last_panid_ = panid; + char buf[5]; + snprintf(buf, sizeof(buf), "%04x", panid); + this->publish_state(buf); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + uint16_t last_panid_; +}; + +class ExtPanIdOpenThreadInfo : public DatasetOpenThreadInfo, public text_sensor::TextSensor { + public: + void update_dataset(otOperationalDataset *dataset) override { + if (!std::equal(this->last_extpanid_.begin(), this->last_extpanid_.end(), dataset->mExtendedPanId.m8)) { + std::copy(dataset->mExtendedPanId.m8, dataset->mExtendedPanId.m8 + 8, this->last_extpanid_.begin()); + this->publish_state(format_hex(this->last_extpanid_.begin(), 8)); + } + } + + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void dump_config() override; + + protected: + std::array last_extpanid_{}; +}; + +} // namespace openthread_info +} // namespace esphome +#endif diff --git a/esphome/components/openthread_info/text_sensor.py b/esphome/components/openthread_info/text_sensor.py new file mode 100644 index 0000000000..ddec8f264c --- /dev/null +++ b/esphome/components/openthread_info/text_sensor.py @@ -0,0 +1,105 @@ +import esphome.codegen as cg +from esphome.components import text_sensor +from esphome.components.openthread.const import ( + CONF_EXT_PAN_ID, + CONF_NETWORK_KEY, + CONF_NETWORK_NAME, + CONF_PAN_ID, +) +import esphome.config_validation as cv +from esphome.const import CONF_CHANNEL, CONF_IP_ADDRESS, ENTITY_CATEGORY_DIAGNOSTIC + +CONF_ROLE = "role" +CONF_RLOC16 = "rloc16" +CONF_EUI64 = "eui64" +CONF_EXT_ADDR = "ext_addr" + + +DEPENDENCIES = ["openthread"] + +openthread_info_ns = cg.esphome_ns.namespace("openthread_info") +IPAddressOpenThreadInfo = openthread_info_ns.class_( + "IPAddressOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +RoleOpenThreadInfo = openthread_info_ns.class_( + "RoleOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +Rloc16OpenThreadInfo = openthread_info_ns.class_( + "Rloc16OpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +ExtAddrOpenThreadInfo = openthread_info_ns.class_( + "ExtAddrOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +Eui64OpenThreadInfo = openthread_info_ns.class_( + "Eui64OpenThreadInfo", text_sensor.TextSensor, cg.Component +) +ChannelOpenThreadInfo = openthread_info_ns.class_( + "ChannelOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +NetworkNameOpenThreadInfo = openthread_info_ns.class_( + "NetworkNameOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +NetworkKeyOpenThreadInfo = openthread_info_ns.class_( + "NetworkKeyOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +PanIdOpenThreadInfo = openthread_info_ns.class_( + "PanIdOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) +ExtPanIdOpenThreadInfo = openthread_info_ns.class_( + "ExtPanIdOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent +) + + +CONFIG_SCHEMA = cv.Schema( + { + cv.Optional(CONF_IP_ADDRESS): text_sensor.text_sensor_schema( + IPAddressOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ), + cv.Optional(CONF_ROLE): text_sensor.text_sensor_schema( + RoleOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_RLOC16): text_sensor.text_sensor_schema( + Rloc16OpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_EXT_ADDR): text_sensor.text_sensor_schema( + ExtAddrOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_EUI64): text_sensor.text_sensor_schema( + Eui64OpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1h")), + cv.Optional(CONF_CHANNEL): text_sensor.text_sensor_schema( + ChannelOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_NETWORK_NAME): text_sensor.text_sensor_schema( + NetworkNameOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_NETWORK_KEY): text_sensor.text_sensor_schema( + NetworkKeyOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_PAN_ID): text_sensor.text_sensor_schema( # noqa: F821 + PanIdOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + cv.Optional(CONF_EXT_PAN_ID): text_sensor.text_sensor_schema( + ExtPanIdOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC + ).extend(cv.polling_component_schema("1s")), + } +) + + +async def setup_conf(config: dict, key: str): + if conf := config.get(key): + var = await text_sensor.new_text_sensor(conf) + await cg.register_component(var, conf) + + +async def to_code(config): + await setup_conf(config, CONF_IP_ADDRESS) + await setup_conf(config, CONF_ROLE) + await setup_conf(config, CONF_RLOC16) + await setup_conf(config, CONF_EXT_ADDR) + await setup_conf(config, CONF_EUI64) + await setup_conf(config, CONF_CHANNEL) + await setup_conf(config, CONF_NETWORK_NAME) + await setup_conf(config, CONF_NETWORK_KEY) + await setup_conf(config, CONF_PAN_ID) + await setup_conf(config, CONF_EXT_PAN_ID) diff --git a/esphome/components/output/float_output.cpp b/esphome/components/output/float_output.cpp index e7dba1d81d..3b83c85716 100644 --- a/esphome/components/output/float_output.cpp +++ b/esphome/components/output/float_output.cpp @@ -1,6 +1,6 @@ #include "float_output.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace output { diff --git a/esphome/components/output/switch/output_switch.cpp b/esphome/components/output/switch/output_switch.cpp index 0e045d18b4..c30cfd3f56 100644 --- a/esphome/components/output/switch/output_switch.cpp +++ b/esphome/components/output/switch/output_switch.cpp @@ -8,7 +8,7 @@ static const char *const TAG = "output.switch"; void OutputSwitch::dump_config() { LOG_SWITCH("", "Output Switch", this); } void OutputSwitch::setup() { - ESP_LOGCONFIG(TAG, "Setting up Output Switch '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); bool initial_state = this->get_initial_state_with_restore_mode().value_or(false); diff --git a/esphome/components/packet_transport/packet_transport.cpp b/esphome/components/packet_transport/packet_transport.cpp index be2f77e379..5c721002b0 100644 --- a/esphome/components/packet_transport/packet_transport.cpp +++ b/esphome/components/packet_transport/packet_transport.cpp @@ -474,10 +474,12 @@ void PacketTransport::process_(const std::vector &data) { } void PacketTransport::dump_config() { - ESP_LOGCONFIG(TAG, "Packet Transport:"); - ESP_LOGCONFIG(TAG, " Platform: %s", this->platform_name_); - ESP_LOGCONFIG(TAG, " Encrypted: %s", YESNO(this->is_encrypted_())); - ESP_LOGCONFIG(TAG, " Ping-pong: %s", YESNO(this->ping_pong_enable_)); + ESP_LOGCONFIG(TAG, + "Packet Transport:\n" + " Platform: %s\n" + " Encrypted: %s\n" + " Ping-pong: %s", + this->platform_name_, YESNO(this->is_encrypted_()), YESNO(this->ping_pong_enable_)); #ifdef USE_SENSOR for (auto sensor : this->sensors_) ESP_LOGCONFIG(TAG, " Sensor: %s", sensor.id); diff --git a/esphome/components/pca6416a/pca6416a.cpp b/esphome/components/pca6416a/pca6416a.cpp index 53c0dcaf76..3e76df5015 100644 --- a/esphome/components/pca6416a/pca6416a.cpp +++ b/esphome/components/pca6416a/pca6416a.cpp @@ -24,7 +24,7 @@ enum PCA6416AGPIORegisters { static const char *const TAG = "pca6416a"; void PCA6416AComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up PCA6416A..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Test to see if device exists uint8_t value; if (!this->read_register_(PCA6416A_INPUT0, &value)) { @@ -60,7 +60,7 @@ void PCA6416AComponent::dump_config() { } LOG_I2C_DEVICE(this) if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with PCA6416A failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } diff --git a/esphome/components/pca9554/pca9554.cpp b/esphome/components/pca9554/pca9554.cpp index 78b877072a..6b3f2d20af 100644 --- a/esphome/components/pca9554/pca9554.cpp +++ b/esphome/components/pca9554/pca9554.cpp @@ -13,7 +13,7 @@ const uint8_t CONFIG_REG = 3; static const char *const TAG = "pca9554"; void PCA9554Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up PCA9554/PCA9554A..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->reg_width_ = (this->pin_count_ + 7) / 8; // Test to see if device exists if (!this->read_inputs_()) { @@ -45,11 +45,13 @@ void PCA9554Component::loop() { } void PCA9554Component::dump_config() { - ESP_LOGCONFIG(TAG, "PCA9554:"); - ESP_LOGCONFIG(TAG, " I/O Pins: %d", this->pin_count_); + ESP_LOGCONFIG(TAG, + "PCA9554:\n" + " I/O Pins: %d", + this->pin_count_); LOG_I2C_DEVICE(this) if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with PCA9554 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } diff --git a/esphome/components/pca9685/pca9685_output.cpp b/esphome/components/pca9685/pca9685_output.cpp index 1998f8d12f..2fe22fd1cc 100644 --- a/esphome/components/pca9685/pca9685_output.cpp +++ b/esphome/components/pca9685/pca9685_output.cpp @@ -1,7 +1,7 @@ #include "pca9685_output.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace pca9685 { @@ -26,9 +26,9 @@ static const uint8_t PCA9685_MODE1_AUTOINC = 0b00100000; static const uint8_t PCA9685_MODE1_SLEEP = 0b00010000; void PCA9685Output::setup() { - ESP_LOGCONFIG(TAG, "Setting up PCA9685OutputComponent..."); + ESP_LOGCONFIG(TAG, "Running setup"); - ESP_LOGV(TAG, " Resetting devices..."); + ESP_LOGV(TAG, " Resetting devices"); if (!this->write_bytes(PCA9685_REGISTER_SOFTWARE_RESET, nullptr, 0)) { this->mark_failed(); return; @@ -83,13 +83,17 @@ void PCA9685Output::setup() { } void PCA9685Output::dump_config() { - ESP_LOGCONFIG(TAG, "PCA9685:"); - ESP_LOGCONFIG(TAG, " Mode: 0x%02X", this->mode_); + ESP_LOGCONFIG(TAG, + "PCA9685:\n" + " Mode: 0x%02X", + this->mode_); if (this->extclk_) { ESP_LOGCONFIG(TAG, " EXTCLK: enabled"); } else { - ESP_LOGCONFIG(TAG, " EXTCLK: disabled"); - ESP_LOGCONFIG(TAG, " Frequency: %.0f Hz", this->frequency_); + ESP_LOGCONFIG(TAG, + " EXTCLK: disabled\n" + " Frequency: %.0f Hz", + this->frequency_); } if (this->is_failed()) { ESP_LOGE(TAG, "Setting up PCA9685 failed!"); diff --git a/esphome/components/pcd8544/pcd_8544.cpp b/esphome/components/pcd8544/pcd_8544.cpp index 5651d60b15..f5b018b127 100644 --- a/esphome/components/pcd8544/pcd_8544.cpp +++ b/esphome/components/pcd8544/pcd_8544.cpp @@ -1,7 +1,7 @@ #include "pcd_8544.h" -#include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace pcd8544 { diff --git a/esphome/components/pcf85063/pcf85063.cpp b/esphome/components/pcf85063/pcf85063.cpp index debc007cb8..d58d35019b 100644 --- a/esphome/components/pcf85063/pcf85063.cpp +++ b/esphome/components/pcf85063/pcf85063.cpp @@ -10,7 +10,7 @@ namespace pcf85063 { static const char *const TAG = "pcf85063"; void PCF85063Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up PCF85063..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->read_rtc_()) { this->mark_failed(); } @@ -22,7 +22,7 @@ void PCF85063Component::dump_config() { ESP_LOGCONFIG(TAG, "PCF85063:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with PCF85063 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); } diff --git a/esphome/components/pcf8563/pcf8563.cpp b/esphome/components/pcf8563/pcf8563.cpp index f2a82735c5..7dd7a6fea8 100644 --- a/esphome/components/pcf8563/pcf8563.cpp +++ b/esphome/components/pcf8563/pcf8563.cpp @@ -10,7 +10,7 @@ namespace pcf8563 { static const char *const TAG = "PCF8563"; void PCF8563Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up PCF8563..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->read_rtc_()) { this->mark_failed(); } @@ -22,7 +22,7 @@ void PCF8563Component::dump_config() { ESP_LOGCONFIG(TAG, "PCF8563:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with PCF8563 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); } diff --git a/esphome/components/pcf8574/pcf8574.cpp b/esphome/components/pcf8574/pcf8574.cpp index 6eaf73e8da..dbab0319d7 100644 --- a/esphome/components/pcf8574/pcf8574.cpp +++ b/esphome/components/pcf8574/pcf8574.cpp @@ -7,7 +7,7 @@ namespace pcf8574 { static const char *const TAG = "pcf8574"; void PCF8574Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up PCF8574..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->read_gpio_()) { ESP_LOGE(TAG, "PCF8574 not available under 0x%02X", this->address_); this->mark_failed(); @@ -22,7 +22,7 @@ void PCF8574Component::dump_config() { LOG_I2C_DEVICE(this) ESP_LOGCONFIG(TAG, " Is PCF8575: %s", YESNO(this->pcf8575_)); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with PCF8574 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } bool PCF8574Component::digital_read(uint8_t pin) { diff --git a/esphome/components/pid/pid_climate.cpp b/esphome/components/pid/pid_climate.cpp index 93b6999a00..8b3be36dcc 100644 --- a/esphome/components/pid/pid_climate.cpp +++ b/esphome/components/pid/pid_climate.cpp @@ -73,15 +73,18 @@ climate::ClimateTraits PIDClimate::traits() { } void PIDClimate::dump_config() { LOG_CLIMATE("", "PID Climate", this); - ESP_LOGCONFIG(TAG, " Control Parameters:"); - ESP_LOGCONFIG(TAG, " kp: %.5f, ki: %.5f, kd: %.5f, output samples: %d", controller_.kp_, controller_.ki_, - controller_.kd_, controller_.output_samples_); + ESP_LOGCONFIG(TAG, + " Control Parameters:\n" + " kp: %.5f, ki: %.5f, kd: %.5f, output samples: %d", + controller_.kp_, controller_.ki_, controller_.kd_, controller_.output_samples_); if (controller_.threshold_low_ == 0 && controller_.threshold_high_ == 0) { ESP_LOGCONFIG(TAG, " Deadband disabled."); } else { - ESP_LOGCONFIG(TAG, " Deadband Parameters:"); - ESP_LOGCONFIG(TAG, " threshold: %0.5f to %0.5f, multipliers(kp: %.5f, ki: %.5f, kd: %.5f), output samples: %d", + ESP_LOGCONFIG(TAG, + " Deadband Parameters:\n" + " threshold: %0.5f to %0.5f, multipliers(kp: %.5f, ki: %.5f, kd: %.5f), " + "output samples: %d", controller_.threshold_low_, controller_.threshold_high_, controller_.kp_multiplier_, controller_.ki_multiplier_, controller_.kd_multiplier_, controller_.deadband_output_samples_); } diff --git a/esphome/components/pid/pid_climate.h b/esphome/components/pid/pid_climate.h index b5275e9775..1a09ffdd20 100644 --- a/esphome/components/pid/pid_climate.h +++ b/esphome/components/pid/pid_climate.h @@ -1,8 +1,8 @@ #pragma once +#include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" -#include "esphome/core/automation.h" #include "esphome/components/climate/climate.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/output/float_output.h" diff --git a/esphome/components/pid/sensor/pid_climate_sensor.cpp b/esphome/components/pid/sensor/pid_climate_sensor.cpp index 2a76c775d3..41ca027d8d 100644 --- a/esphome/components/pid/sensor/pid_climate_sensor.cpp +++ b/esphome/components/pid/sensor/pid_climate_sensor.cpp @@ -1,6 +1,6 @@ #include "pid_climate_sensor.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace pid { diff --git a/esphome/components/pipsolar/output/pipsolar_output.cpp b/esphome/components/pipsolar/output/pipsolar_output.cpp index b843f1f3e6..00ec73b56a 100644 --- a/esphome/components/pipsolar/output/pipsolar_output.cpp +++ b/esphome/components/pipsolar/output/pipsolar_output.cpp @@ -1,6 +1,6 @@ #include "pipsolar_output.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace pipsolar { diff --git a/esphome/components/pipsolar/pipsolar.cpp b/esphome/components/pipsolar/pipsolar.cpp index 03c699e7d4..40405114a4 100644 --- a/esphome/components/pipsolar/pipsolar.cpp +++ b/esphome/components/pipsolar/pipsolar.cpp @@ -1,6 +1,6 @@ #include "pipsolar.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace pipsolar { @@ -861,8 +861,8 @@ void Pipsolar::switch_command(const std::string &command) { queue_command_(command.c_str(), command.length()); } void Pipsolar::dump_config() { - ESP_LOGCONFIG(TAG, "Pipsolar:"); - ESP_LOGCONFIG(TAG, "used commands:"); + ESP_LOGCONFIG(TAG, "Pipsolar:\n" + "used commands:"); for (auto &used_polling_command : this->used_polling_commands_) { if (used_polling_command.length != 0) { ESP_LOGCONFIG(TAG, "%s", used_polling_command.command); diff --git a/esphome/components/pm1006/pm1006.cpp b/esphome/components/pm1006/pm1006.cpp index 1a89307eee..c466c4bb25 100644 --- a/esphome/components/pm1006/pm1006.cpp +++ b/esphome/components/pm1006/pm1006.cpp @@ -55,8 +55,8 @@ uint8_t PM1006Component::pm1006_checksum_(const uint8_t *command_data, uint8_t l } optional PM1006Component::check_byte_() const { - uint8_t index = this->data_index_; - uint8_t byte = this->data_[index]; + const uint8_t index = this->data_index_; + const uint8_t byte = this->data_[index]; // index 0..2 are the fixed header if (index < sizeof(PM1006_RESPONSE_HEADER)) { @@ -86,7 +86,7 @@ optional PM1006Component::check_byte_() const { } void PM1006Component::parse_data_() { - const int pm_2_5_concentration = this->get_16_bit_uint_(5); + const uint16_t pm_2_5_concentration = this->get_16_bit_uint_(5); ESP_LOGD(TAG, "Got PM2.5 Concentration: %d µg/m³", pm_2_5_concentration); @@ -95,9 +95,5 @@ void PM1006Component::parse_data_() { } } -uint16_t PM1006Component::get_16_bit_uint_(uint8_t start_index) const { - return encode_uint16(this->data_[start_index], this->data_[start_index + 1]); -} - } // namespace pm1006 } // namespace esphome diff --git a/esphome/components/pm1006/pm1006.h b/esphome/components/pm1006/pm1006.h index 238ac67006..98637dad71 100644 --- a/esphome/components/pm1006/pm1006.h +++ b/esphome/components/pm1006/pm1006.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/uart/uart.h" @@ -22,8 +23,10 @@ class PM1006Component : public PollingComponent, public uart::UARTDevice { protected: optional check_byte_() const; void parse_data_(); - uint16_t get_16_bit_uint_(uint8_t start_index) const; uint8_t pm1006_checksum_(const uint8_t *command_data, uint8_t length) const; + uint16_t get_16_bit_uint_(uint8_t start_index) const { + return encode_uint16(this->data_[start_index], this->data_[start_index + 1]); + } sensor::Sensor *pm_2_5_sensor_{nullptr}; diff --git a/esphome/components/pm2005/pm2005.cpp b/esphome/components/pm2005/pm2005.cpp index 38847210fd..57c616c4c6 100644 --- a/esphome/components/pm2005/pm2005.cpp +++ b/esphome/components/pm2005/pm2005.cpp @@ -39,17 +39,14 @@ static const LogString *pm2005_get_measuring_mode_string(int status) { static inline uint16_t get_sensor_value(const uint8_t *data, uint8_t i) { return data[i] * 0x100 + data[i + 1]; } void PM2005Component::setup() { + ESP_LOGCONFIG(TAG, "Running setup"); if (this->sensor_type_ == PM2005) { - ESP_LOGCONFIG(TAG, "Setting up PM2005..."); - this->situation_value_index_ = 3; this->pm_1_0_value_index_ = 4; this->pm_2_5_value_index_ = 6; this->pm_10_0_value_index_ = 8; this->measuring_value_index_ = 10; } else { - ESP_LOGCONFIG(TAG, "Setting up PM2105..."); - this->situation_value_index_ = 2; this->pm_1_0_value_index_ = 3; this->pm_2_5_value_index_ = 5; @@ -58,7 +55,7 @@ void PM2005Component::setup() { } if (this->read(this->data_buffer_, 12) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Communication failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); return; } @@ -66,7 +63,7 @@ void PM2005Component::setup() { void PM2005Component::update() { if (this->read(this->data_buffer_, 12) != i2c::ERROR_OK) { - ESP_LOGW(TAG, "Read result failed."); + ESP_LOGW(TAG, "Read result failed"); this->status_set_warning(); return; } @@ -85,10 +82,10 @@ void PM2005Component::update() { return; } - uint16_t pm1 = get_sensor_value(this->data_buffer_, this->pm_1_0_value_index_); - uint16_t pm25 = get_sensor_value(this->data_buffer_, this->pm_2_5_value_index_); - uint16_t pm10 = get_sensor_value(this->data_buffer_, this->pm_10_0_value_index_); - uint16_t sensor_measuring_mode = get_sensor_value(this->data_buffer_, this->measuring_value_index_); + const uint16_t pm1 = get_sensor_value(this->data_buffer_, this->pm_1_0_value_index_); + const uint16_t pm25 = get_sensor_value(this->data_buffer_, this->pm_2_5_value_index_); + const uint16_t pm10 = get_sensor_value(this->data_buffer_, this->pm_10_0_value_index_); + const uint16_t sensor_measuring_mode = get_sensor_value(this->data_buffer_, this->measuring_value_index_); ESP_LOGD(TAG, "PM1.0: %d, PM2.5: %d, PM10: %d, Measuring mode: %s.", pm1, pm25, pm10, LOG_STR_ARG(pm2005_get_measuring_mode_string(sensor_measuring_mode))); @@ -106,12 +103,14 @@ void PM2005Component::update() { } void PM2005Component::dump_config() { - ESP_LOGCONFIG(TAG, "PM2005:"); - ESP_LOGCONFIG(TAG, " Type: PM2%u05", this->sensor_type_ == PM2105); + ESP_LOGCONFIG(TAG, + "PM2005:\n" + " Type: PM2%u05", + this->sensor_type_ == PM2105); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with PM2%u05 failed!", this->sensor_type_ == PM2105); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_SENSOR(" ", "PM1.0", this->pm_1_0_sensor_); diff --git a/esphome/components/pm2005/sensor.py b/esphome/components/pm2005/sensor.py index 66f630f8ff..3a650560a0 100644 --- a/esphome/components/pm2005/sensor.py +++ b/esphome/components/pm2005/sensor.py @@ -1,8 +1,8 @@ """PM2005/2105 Sensor component for ESPHome.""" import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import i2c, sensor +import esphome.config_validation as cv from esphome.const import ( CONF_ID, CONF_PM_1_0, diff --git a/esphome/components/pmsa003i/pmsa003i.cpp b/esphome/components/pmsa003i/pmsa003i.cpp index 36f9c9a132..4702c0cf5f 100644 --- a/esphome/components/pmsa003i/pmsa003i.cpp +++ b/esphome/components/pmsa003i/pmsa003i.cpp @@ -1,6 +1,6 @@ #include "pmsa003i.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include namespace esphome { @@ -19,7 +19,7 @@ static const uint8_t START_CHARACTER_2 = 0x4D; static const uint8_t READ_DATA_RETRY_COUNT = 3; void PMSA003IComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up pmsa003i..."); + ESP_LOGCONFIG(TAG, "Running setup"); PM25AQIData data; bool successful_read = this->read_data_(&data); diff --git a/esphome/components/pmsx003/pmsx003.cpp b/esphome/components/pmsx003/pmsx003.cpp index 0abed8a5a4..eb10d19c91 100644 --- a/esphome/components/pmsx003/pmsx003.cpp +++ b/esphome/components/pmsx003/pmsx003.cpp @@ -314,9 +314,5 @@ void PMSX003Component::parse_data_() { this->status_clear_warning(); } -uint16_t PMSX003Component::get_16_bit_uint_(uint8_t start_index) { - return (uint16_t(this->data_[start_index]) << 8) | uint16_t(this->data_[start_index + 1]); -} - } // namespace pmsx003 } // namespace esphome diff --git a/esphome/components/pmsx003/pmsx003.h b/esphome/components/pmsx003/pmsx003.h index 85bb1ff9f3..e422d4165b 100644 --- a/esphome/components/pmsx003/pmsx003.h +++ b/esphome/components/pmsx003/pmsx003.h @@ -1,8 +1,9 @@ #pragma once +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/uart/uart.h" -#include "esphome/core/component.h" namespace esphome { namespace pmsx003 { @@ -77,7 +78,9 @@ class PMSX003Component : public uart::UARTDevice, public Component { void parse_data_(); bool check_payload_length_(uint16_t payload_length); void send_command_(PMSX0003Command cmd, uint16_t data); - uint16_t get_16_bit_uint_(uint8_t start_index); + uint16_t get_16_bit_uint_(uint8_t start_index) const { + return encode_uint16(this->data_[start_index], this->data_[start_index + 1]); + } uint8_t data_[64]; uint8_t data_index_{0}; diff --git a/esphome/components/pmwcs3/pmwcs3.cpp b/esphome/components/pmwcs3/pmwcs3.cpp index 97ce4c9ae0..95638851b5 100644 --- a/esphome/components/pmwcs3/pmwcs3.cpp +++ b/esphome/components/pmwcs3/pmwcs3.cpp @@ -26,33 +26,31 @@ static const char *const TAG = "pmwcs3"; void PMWCS3Component::new_i2c_address(uint8_t address) { if (!this->write_byte(PMWCS3_SET_I2C_ADDRESS, address)) { this->status_set_warning(); - ESP_LOGW(TAG, "couldn't write the new I2C address %d", address); + ESP_LOGW(TAG, "Setting I2C address failed (%d)", address); return; } this->set_i2c_address(address); // Allows device to continue working until new firmware is written with new address. - ESP_LOGVV(TAG, "changed I2C address to %d", address); + ESP_LOGVV(TAG, "Set I2C address to %d", address); this->status_clear_warning(); } void PMWCS3Component::air_calibration() { if (!this->write_bytes(PMWCS3_REG_CALIBRATE_AIR, nullptr, 0)) { this->status_set_warning(); - ESP_LOGW(TAG, "couldn't start air calibration"); + ESP_LOGW(TAG, "Starting air calibration failed"); return; } - ESP_LOGW(TAG, "Start air calibration during the next 300s"); + ESP_LOGW(TAG, "Running air calibration for 300s"); } void PMWCS3Component::water_calibration() { if (!this->write_bytes(PMWCS3_REG_CALIBRATE_WATER, nullptr, 0)) { this->status_set_warning(); - ESP_LOGW(TAG, "couldn't start water calibration"); + ESP_LOGW(TAG, "Starting water calibration failed"); return; } - ESP_LOGW(TAG, "Start water calibration during the next 300s"); + ESP_LOGW(TAG, "Running water calibration for 300s"); } -void PMWCS3Component::setup() { ESP_LOGCONFIG(TAG, "Setting up PMWCS3..."); } - void PMWCS3Component::update() { this->read_data_(); } float PMWCS3Component::get_setup_priority() const { return setup_priority::DATA; } @@ -61,10 +59,8 @@ void PMWCS3Component::dump_config() { ESP_LOGCONFIG(TAG, "PMWCS3"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with PMWCS3 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } - ESP_LOGI(TAG, "%s", this->is_failed() ? "FAILED" : "OK"); - LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "e25", this->e25_sensor_); LOG_SENSOR(" ", "ec", this->ec_sensor_); @@ -75,7 +71,7 @@ void PMWCS3Component::read_data_() { /////// Super important !!!! first activate reading PMWCS3_REG_READ_START (if not, return always the same values) //// if (!this->write_bytes(PMWCS3_REG_READ_START, nullptr, 0)) { this->status_set_warning(); - ESP_LOGVV(TAG, "Failed to write into REG_READ_START register !!!"); + ESP_LOGVV(TAG, "Writing REG_READ_START failed"); return; } @@ -85,7 +81,7 @@ void PMWCS3Component::read_data_() { uint8_t data[8]; float e25, ec, temperature, vwc; if (!this->read_bytes(PMWCS3_REG_GET_DATA, (uint8_t *) &data, 8)) { - ESP_LOGVV(TAG, "Error reading PMWCS3_REG_GET_DATA registers"); + ESP_LOGVV(TAG, "Reading PMWCS3_REG_GET_DATA failed"); this->mark_failed(); return; } diff --git a/esphome/components/pmwcs3/pmwcs3.h b/esphome/components/pmwcs3/pmwcs3.h index f3916bb868..d60f9d1f61 100644 --- a/esphome/components/pmwcs3/pmwcs3.h +++ b/esphome/components/pmwcs3/pmwcs3.h @@ -12,7 +12,6 @@ namespace pmwcs3 { class PMWCS3Component : public PollingComponent, public i2c::I2CDevice { public: - void setup() override; void update() override; void dump_config() override; float get_setup_priority() const override; diff --git a/esphome/components/pn532/pn532.cpp b/esphome/components/pn532/pn532.cpp index 8088e6c022..da5598bf10 100644 --- a/esphome/components/pn532/pn532.cpp +++ b/esphome/components/pn532/pn532.cpp @@ -15,11 +15,11 @@ namespace pn532 { static const char *const TAG = "pn532"; void PN532::setup() { - ESP_LOGCONFIG(TAG, "Setting up PN532..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Get version data if (!this->write_command_({PN532_COMMAND_VERSION_DATA})) { - ESP_LOGW(TAG, "Error sending version command, trying again..."); + ESP_LOGW(TAG, "Error sending version command, trying again"); if (!this->write_command_({PN532_COMMAND_VERSION_DATA})) { ESP_LOGE(TAG, "Error sending version command"); this->mark_failed(); @@ -208,21 +208,21 @@ void PN532::loop() { } } } else if (next_task_ == CLEAN) { - ESP_LOGD(TAG, " Tag cleaning..."); + ESP_LOGD(TAG, " Tag cleaning"); if (!this->clean_tag_(nfcid)) { ESP_LOGE(TAG, " Tag was not fully cleaned successfully"); } ESP_LOGD(TAG, " Tag cleaned!"); } else if (next_task_ == FORMAT) { - ESP_LOGD(TAG, " Tag formatting..."); + ESP_LOGD(TAG, " Tag formatting"); if (!this->format_tag_(nfcid)) { ESP_LOGE(TAG, "Error formatting tag as NDEF"); } ESP_LOGD(TAG, " Tag formatted!"); } else if (next_task_ == WRITE) { if (this->next_task_message_to_write_ != nullptr) { - ESP_LOGD(TAG, " Tag writing..."); - ESP_LOGD(TAG, " Tag formatting..."); + ESP_LOGD(TAG, " Tag writing"); + ESP_LOGD(TAG, " Tag formatting"); if (!this->format_tag_(nfcid)) { ESP_LOGE(TAG, " Tag could not be formatted for writing"); } else { @@ -281,7 +281,7 @@ bool PN532::write_command_(const std::vector &data) { } bool PN532::read_ack_() { - ESP_LOGV(TAG, "Reading ACK..."); + ESP_LOGV(TAG, "Reading ACK"); std::vector data; if (!this->read_data(data, 6)) { diff --git a/esphome/components/pn532/pn532.h b/esphome/components/pn532/pn532.h index 8194d86477..c8e9a40008 100644 --- a/esphome/components/pn532/pn532.h +++ b/esphome/components/pn532/pn532.h @@ -38,7 +38,7 @@ class PN532 : public PollingComponent { float get_setup_priority() const override; void loop() override; - void on_shutdown() override { powerdown(); } + void on_powerdown() override { powerdown(); } void register_tag(PN532BinarySensor *tag) { this->binary_sensors_.push_back(tag); } void register_ontag_trigger(nfc::NfcOnTagTrigger *trig) { this->triggers_ontag_.push_back(trig); } diff --git a/esphome/components/pn532_spi/pn532_spi.cpp b/esphome/components/pn532_spi/pn532_spi.cpp index d55d8161d8..2e66d4ed83 100644 --- a/esphome/components/pn532_spi/pn532_spi.cpp +++ b/esphome/components/pn532_spi/pn532_spi.cpp @@ -51,7 +51,7 @@ bool PN532Spi::read_data(std::vector &data, uint8_t len) { delay(2); this->write_byte(0x03); - ESP_LOGV(TAG, "Reading data..."); + ESP_LOGV(TAG, "Reading data"); data.resize(len); this->read_array(data.data(), len); diff --git a/esphome/components/pn7150/pn7150.cpp b/esphome/components/pn7150/pn7150.cpp index be4d6c1bb7..971ddd23cb 100644 --- a/esphome/components/pn7150/pn7150.cpp +++ b/esphome/components/pn7150/pn7150.cpp @@ -830,7 +830,7 @@ void PN7150::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi switch (this->next_task_) { case EP_CLEAN: - ESP_LOGD(TAG, " Tag cleaning..."); + ESP_LOGD(TAG, " Tag cleaning"); if (this->clean_endpoint_(working_endpoint.tag->get_uid()) != nfc::STATUS_OK) { ESP_LOGE(TAG, " Tag cleaning incomplete"); } @@ -838,7 +838,7 @@ void PN7150::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi break; case EP_FORMAT: - ESP_LOGD(TAG, " Tag formatting..."); + ESP_LOGD(TAG, " Tag formatting"); if (this->format_endpoint_(working_endpoint.tag->get_uid()) != nfc::STATUS_OK) { ESP_LOGE(TAG, "Error formatting tag as NDEF"); } @@ -847,8 +847,8 @@ void PN7150::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi case EP_WRITE: if (this->next_task_message_to_write_ != nullptr) { - ESP_LOGD(TAG, " Tag writing..."); - ESP_LOGD(TAG, " Tag formatting..."); + ESP_LOGD(TAG, " Tag writing"); + ESP_LOGD(TAG, " Tag formatting"); if (this->format_endpoint_(working_endpoint.tag->get_uid()) != nfc::STATUS_OK) { ESP_LOGE(TAG, " Tag could not be formatted for writing"); } else { diff --git a/esphome/components/pn7160/pn7160.cpp b/esphome/components/pn7160/pn7160.cpp index a7d3b38fb7..2a1de20657 100644 --- a/esphome/components/pn7160/pn7160.cpp +++ b/esphome/components/pn7160/pn7160.cpp @@ -854,7 +854,7 @@ void PN7160::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi switch (this->next_task_) { case EP_CLEAN: - ESP_LOGD(TAG, " Tag cleaning..."); + ESP_LOGD(TAG, " Tag cleaning"); if (this->clean_endpoint_(working_endpoint.tag->get_uid()) != nfc::STATUS_OK) { ESP_LOGE(TAG, " Tag cleaning incomplete"); } @@ -862,7 +862,7 @@ void PN7160::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi break; case EP_FORMAT: - ESP_LOGD(TAG, " Tag formatting..."); + ESP_LOGD(TAG, " Tag formatting"); if (this->format_endpoint_(working_endpoint.tag->get_uid()) != nfc::STATUS_OK) { ESP_LOGE(TAG, "Error formatting tag as NDEF"); } @@ -871,8 +871,8 @@ void PN7160::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi case EP_WRITE: if (this->next_task_message_to_write_ != nullptr) { - ESP_LOGD(TAG, " Tag writing..."); - ESP_LOGD(TAG, " Tag formatting..."); + ESP_LOGD(TAG, " Tag writing"); + ESP_LOGD(TAG, " Tag formatting"); if (this->format_endpoint_(working_endpoint.tag->get_uid()) != nfc::STATUS_OK) { ESP_LOGE(TAG, " Tag could not be formatted for writing"); } else { diff --git a/esphome/components/power_supply/power_supply.cpp b/esphome/components/power_supply/power_supply.cpp index 7474075302..6fbadc73ae 100644 --- a/esphome/components/power_supply/power_supply.cpp +++ b/esphome/components/power_supply/power_supply.cpp @@ -7,7 +7,7 @@ namespace power_supply { static const char *const TAG = "power_supply"; void PowerSupply::setup() { - ESP_LOGCONFIG(TAG, "Setting up Power Supply..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->pin_->setup(); this->pin_->digital_write(false); @@ -17,8 +17,10 @@ void PowerSupply::setup() { void PowerSupply::dump_config() { ESP_LOGCONFIG(TAG, "Power Supply:"); LOG_PIN(" Pin: ", this->pin_); - ESP_LOGCONFIG(TAG, " Time to enable: %" PRIu32 " ms", this->enable_time_); - ESP_LOGCONFIG(TAG, " Keep on time: %.1f s", this->keep_on_time_ / 1000.0f); + ESP_LOGCONFIG(TAG, + " Time to enable: %" PRIu32 " ms\n" + " Keep on time: %.1f s", + this->enable_time_, this->keep_on_time_ / 1000.0f); if (this->enable_on_boot_) ESP_LOGCONFIG(TAG, " Enabled at startup: True"); } @@ -50,7 +52,7 @@ void PowerSupply::unrequest_high_power() { }); } } -void PowerSupply::on_shutdown() { +void PowerSupply::on_powerdown() { this->active_requests_ = 0; this->pin_->digital_write(false); } diff --git a/esphome/components/power_supply/power_supply.h b/esphome/components/power_supply/power_supply.h index 0b06105ae9..3959f6f299 100644 --- a/esphome/components/power_supply/power_supply.h +++ b/esphome/components/power_supply/power_supply.h @@ -32,7 +32,7 @@ class PowerSupply : public Component { /// Hardware setup priority (+1). float get_setup_priority() const override; - void on_shutdown() override; + void on_powerdown() override; protected: GPIOPin *pin_; diff --git a/esphome/components/prometheus/__init__.py b/esphome/components/prometheus/__init__.py index b899fe7642..26a9e70f7c 100644 --- a/esphome/components/prometheus/__init__.py +++ b/esphome/components/prometheus/__init__.py @@ -31,7 +31,6 @@ CONFIG_SCHEMA = cv.Schema( } ), }, - cv.only_with_arduino, ).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/psram/__init__.py b/esphome/components/psram/__init__.py index 53ba54dd28..9299cdcd0e 100644 --- a/esphome/components/psram/__init__.py +++ b/esphome/components/psram/__init__.py @@ -2,13 +2,17 @@ import logging import esphome.codegen as cg from esphome.components.esp32 import ( + CONF_CPU_FREQUENCY, CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES, VARIANT_ESP32, add_idf_sdkconfig_option, get_esp32_variant, - only_on_variant, ) -from esphome.components.esp32.const import VARIANT_ESP32S2, VARIANT_ESP32S3 +from esphome.components.esp32.const import ( + VARIANT_ESP32P4, + VARIANT_ESP32S2, + VARIANT_ESP32S3, +) import esphome.config_validation as cv from esphome.const import ( CONF_ADVANCED, @@ -34,34 +38,46 @@ PsramComponent = psram_ns.class_("PsramComponent", cg.Component) TYPE_QUAD = "quad" TYPE_OCTAL = "octal" +TYPE_HEX = "hex" + +SDK_MODES = {TYPE_QUAD: "QUAD", TYPE_OCTAL: "OCT", TYPE_HEX: "HEX"} CONF_ENABLE_ECC = "enable_ecc" SPIRAM_MODES = { - TYPE_QUAD: "CONFIG_SPIRAM_MODE_QUAD", - TYPE_OCTAL: "CONFIG_SPIRAM_MODE_OCT", + VARIANT_ESP32: (TYPE_QUAD,), + VARIANT_ESP32S2: (TYPE_QUAD,), + VARIANT_ESP32S3: (TYPE_QUAD, TYPE_OCTAL), + VARIANT_ESP32P4: (TYPE_HEX,), } + SPIRAM_SPEEDS = { - 40e6: "CONFIG_SPIRAM_SPEED_40M", - 80e6: "CONFIG_SPIRAM_SPEED_80M", - 120e6: "CONFIG_SPIRAM_SPEED_120M", + VARIANT_ESP32: (40, 80, 120), + VARIANT_ESP32S2: (40, 80, 120), + VARIANT_ESP32S3: (40, 80, 120), + VARIANT_ESP32P4: (20, 100, 200), } def validate_psram_mode(config): - if config[CONF_MODE] == TYPE_OCTAL and config[CONF_SPEED] == 120e6: - esp32_config = fv.full_config.get()[PLATFORM_ESP32] - if ( - esp32_config[CONF_FRAMEWORK] - .get(CONF_ADVANCED, {}) - .get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES) - ): - _LOGGER.warning( - "120MHz PSRAM in octal mode is an experimental feature - use at your own risk" + esp32_config = fv.full_config.get()[PLATFORM_ESP32] + if config[CONF_SPEED] == "120MHZ": + if esp32_config[CONF_CPU_FREQUENCY] != "240MHZ": + raise cv.Invalid( + "PSRAM 120MHz requires 240MHz CPU frequency (set in esp32 component)" ) - else: - raise cv.Invalid("PSRAM 120MHz is not supported in octal mode") + if config[CONF_MODE] == TYPE_OCTAL: + if ( + esp32_config[CONF_FRAMEWORK] + .get(CONF_ADVANCED, {}) + .get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES) + ): + _LOGGER.warning( + "120MHz PSRAM in octal mode is an experimental feature - use at your own risk" + ) + else: + raise cv.Invalid("PSRAM 120MHz is not supported in octal mode") if config[CONF_MODE] != TYPE_OCTAL and config[CONF_ENABLE_ECC]: raise cv.Invalid("ECC is only available in octal mode.") if config[CONF_MODE] == TYPE_OCTAL: @@ -73,23 +89,23 @@ def validate_psram_mode(config): return config -CONFIG_SCHEMA = cv.All( - cv.Schema( +def get_config_schema(config): + variant = get_esp32_variant() + speeds = [f"{s}MHZ" for s in SPIRAM_SPEEDS.get(variant, [])] + if not speeds: + return cv.Invalid("PSRAM is not supported on this chip") + modes = SPIRAM_MODES[variant] + return cv.Schema( { cv.GenerateID(): cv.declare_id(PsramComponent), - cv.Optional(CONF_MODE, default=TYPE_QUAD): cv.enum( - SPIRAM_MODES, lower=True - ), + cv.Optional(CONF_MODE, default=modes[0]): cv.one_of(*modes, lower=True), cv.Optional(CONF_ENABLE_ECC, default=False): cv.boolean, - cv.Optional(CONF_SPEED, default=40e6): cv.All( - cv.frequency, cv.one_of(*SPIRAM_SPEEDS) - ), + cv.Optional(CONF_SPEED, default=speeds[0]): cv.one_of(*speeds, upper=True), } - ), - only_on_variant( - supported=[VARIANT_ESP32, VARIANT_ESP32S3, VARIANT_ESP32S2], - ), -) + )(config) + + +CONFIG_SCHEMA = get_config_schema FINAL_VALIDATE_SCHEMA = validate_psram_mode @@ -104,15 +120,23 @@ async def to_code(config): add_idf_sdkconfig_option( f"CONFIG_{get_esp32_variant().upper()}_SPIRAM_SUPPORT", True ) + add_idf_sdkconfig_option("CONFIG_SOC_SPIRAM_SUPPORTED", True) add_idf_sdkconfig_option("CONFIG_SPIRAM", True) add_idf_sdkconfig_option("CONFIG_SPIRAM_USE", True) add_idf_sdkconfig_option("CONFIG_SPIRAM_USE_CAPS_ALLOC", True) add_idf_sdkconfig_option("CONFIG_SPIRAM_IGNORE_NOTFOUND", True) - add_idf_sdkconfig_option(f"{SPIRAM_MODES[config[CONF_MODE]]}", True) - add_idf_sdkconfig_option(f"{SPIRAM_SPEEDS[config[CONF_SPEED]]}", True) - if config[CONF_MODE] == TYPE_OCTAL and config[CONF_SPEED] == 120e6: - add_idf_sdkconfig_option("CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240", True) + add_idf_sdkconfig_option( + f"CONFIG_SPIRAM_MODE_{SDK_MODES[config[CONF_MODE]]}", True + ) + + # Remove MHz suffix, convert to int + speed = int(config[CONF_SPEED][:-3]) + add_idf_sdkconfig_option(f"CONFIG_SPIRAM_SPEED_{speed}M", True) + add_idf_sdkconfig_option("CONFIG_SPIRAM_SPEED", speed) + if config[CONF_MODE] == TYPE_OCTAL and speed == 120: + add_idf_sdkconfig_option("CONFIG_ESPTOOLPY_FLASHFREQ_120M", True) + add_idf_sdkconfig_option("CONFIG_BOOTLOADER_FLASH_DC_AWARE", True) if CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version(5, 4, 0): add_idf_sdkconfig_option( "CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR", True diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.cpp b/esphome/components/pulse_counter/pulse_counter_sensor.cpp index 2bc80c352c..bfca0c6a4e 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.cpp +++ b/esphome/components/pulse_counter/pulse_counter_sensor.cpp @@ -156,7 +156,7 @@ pulse_counter_t HwPulseCounterStorage::read_raw_value() { #endif // HAS_PCNT void PulseCounterSensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up pulse counter '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); if (!this->storage_.pulse_counter_setup(this->pin_)) { this->mark_failed(); return; @@ -171,9 +171,12 @@ void PulseCounterSensor::set_total_pulses(uint32_t pulses) { void PulseCounterSensor::dump_config() { LOG_SENSOR("", "Pulse Counter", this); LOG_PIN(" Pin: ", this->pin_); - ESP_LOGCONFIG(TAG, " Rising Edge: %s", EDGE_MODE_TO_STRING[this->storage_.rising_edge_mode]); - ESP_LOGCONFIG(TAG, " Falling Edge: %s", EDGE_MODE_TO_STRING[this->storage_.falling_edge_mode]); - ESP_LOGCONFIG(TAG, " Filtering pulses shorter than %" PRIu32 " µs", this->storage_.filter_us); + ESP_LOGCONFIG(TAG, + " Rising Edge: %s\n" + " Falling Edge: %s\n" + " Filtering pulses shorter than %" PRIu32 " µs", + EDGE_MODE_TO_STRING[this->storage_.rising_edge_mode], + EDGE_MODE_TO_STRING[this->storage_.falling_edge_mode], this->storage_.filter_us); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp b/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp index 1856a023cc..74f63a9640 100644 --- a/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp +++ b/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp @@ -8,11 +8,14 @@ namespace pvvx_mithermometer { static const char *const TAG = "display.pvvx_mithermometer"; void PVVXDisplay::dump_config() { - ESP_LOGCONFIG(TAG, "PVVX MiThermometer display:"); - ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent_->address_str().c_str()); - ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Characteristic UUID : %s", this->char_uuid_.to_string().c_str()); - ESP_LOGCONFIG(TAG, " Auto clear : %s", YESNO(this->auto_clear_enabled_)); + ESP_LOGCONFIG(TAG, + "PVVX MiThermometer display:\n" + " MAC address : %s\n" + " Service UUID : %s\n" + " Characteristic UUID : %s\n" + " Auto clear : %s", + this->parent_->address_str().c_str(), this->service_uuid_.to_string().c_str(), + this->char_uuid_.to_string().c_str(), YESNO(this->auto_clear_enabled_)); #ifdef USE_TIME ESP_LOGCONFIG(TAG, " Set time on connection: %s", YESNO(this->time_ != nullptr)); #endif diff --git a/esphome/components/pylontech/pylontech.cpp b/esphome/components/pylontech/pylontech.cpp index b33f4d4874..ef3de069ca 100644 --- a/esphome/components/pylontech/pylontech.cpp +++ b/esphome/components/pylontech/pylontech.cpp @@ -1,6 +1,6 @@ #include "pylontech.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace pylontech { @@ -26,7 +26,7 @@ void PylontechComponent::dump_config() { } void PylontechComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up pylontech..."); + ESP_LOGCONFIG(TAG, "Running setup"); while (this->available() != 0) { this->read(); } diff --git a/esphome/components/pylontech/sensor/pylontech_sensor.cpp b/esphome/components/pylontech/sensor/pylontech_sensor.cpp index 5b5db0731e..11437369ed 100644 --- a/esphome/components/pylontech/sensor/pylontech_sensor.cpp +++ b/esphome/components/pylontech/sensor/pylontech_sensor.cpp @@ -10,8 +10,10 @@ static const char *const TAG = "pylontech.sensor"; PylontechSensor::PylontechSensor(int8_t bat_num) { this->bat_num_ = bat_num; } void PylontechSensor::dump_config() { - ESP_LOGCONFIG(TAG, "Pylontech Sensor:"); - ESP_LOGCONFIG(TAG, " Battery %d", this->bat_num_); + ESP_LOGCONFIG(TAG, + "Pylontech Sensor:\n" + " Battery %d", + this->bat_num_); LOG_SENSOR(" ", "Voltage", this->voltage_sensor_); LOG_SENSOR(" ", "Current", this->current_sensor_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); diff --git a/esphome/components/pylontech/text_sensor/pylontech_text_sensor.cpp b/esphome/components/pylontech/text_sensor/pylontech_text_sensor.cpp index 9e894bc570..55e02f3e33 100644 --- a/esphome/components/pylontech/text_sensor/pylontech_text_sensor.cpp +++ b/esphome/components/pylontech/text_sensor/pylontech_text_sensor.cpp @@ -10,8 +10,10 @@ static const char *const TAG = "pylontech.textsensor"; PylontechTextSensor::PylontechTextSensor(int8_t bat_num) { this->bat_num_ = bat_num; } void PylontechTextSensor::dump_config() { - ESP_LOGCONFIG(TAG, "Pylontech Text Sensor:"); - ESP_LOGCONFIG(TAG, " Battery %d", this->bat_num_); + ESP_LOGCONFIG(TAG, + "Pylontech Text Sensor:\n" + " Battery %d", + this->bat_num_); LOG_TEXT_SENSOR(" ", "Base state", this->base_state_text_sensor_); LOG_TEXT_SENSOR(" ", "Voltage state", this->voltage_state_text_sensor_); LOG_TEXT_SENSOR(" ", "Current state", this->current_state_text_sensor_); diff --git a/esphome/components/pzemac/pzemac.cpp b/esphome/components/pzemac/pzemac.cpp index c3738d1852..0dbe0e761d 100644 --- a/esphome/components/pzemac/pzemac.cpp +++ b/esphome/components/pzemac/pzemac.cpp @@ -64,8 +64,10 @@ void PZEMAC::on_modbus_data(const std::vector &data) { void PZEMAC::update() { this->send(PZEM_CMD_READ_IN_REGISTERS, 0, PZEM_REGISTER_COUNT); } void PZEMAC::dump_config() { - ESP_LOGCONFIG(TAG, "PZEMAC:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "PZEMAC:\n" + " Address: 0x%02X", + this->address_); LOG_SENSOR("", "Voltage", this->voltage_sensor_); LOG_SENSOR("", "Current", this->current_sensor_); LOG_SENSOR("", "Power", this->power_sensor_); diff --git a/esphome/components/pzemdc/pzemdc.cpp b/esphome/components/pzemdc/pzemdc.cpp index 28e4210ff7..428bcc1fcf 100644 --- a/esphome/components/pzemdc/pzemdc.cpp +++ b/esphome/components/pzemdc/pzemdc.cpp @@ -54,8 +54,10 @@ void PZEMDC::on_modbus_data(const std::vector &data) { void PZEMDC::update() { this->send(PZEM_CMD_READ_IN_REGISTERS, 0, 8); } void PZEMDC::dump_config() { - ESP_LOGCONFIG(TAG, "PZEMDC:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "PZEMDC:\n" + " Address: 0x%02X", + this->address_); LOG_SENSOR("", "Voltage", this->voltage_sensor_); LOG_SENSOR("", "Current", this->current_sensor_); LOG_SENSOR("", "Power", this->power_sensor_); diff --git a/esphome/components/qmc5883l/qmc5883l.cpp b/esphome/components/qmc5883l/qmc5883l.cpp index 36286244fb..e41d7de644 100644 --- a/esphome/components/qmc5883l/qmc5883l.cpp +++ b/esphome/components/qmc5883l/qmc5883l.cpp @@ -24,7 +24,7 @@ static const uint8_t QMC5883L_REGISTER_CONTROL_2 = 0x0A; static const uint8_t QMC5883L_REGISTER_PERIOD = 0x0B; void QMC5883LComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up QMC5883L..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Soft Reset if (!this->write_byte(QMC5883L_REGISTER_CONTROL_2, 1 << 7)) { this->error_code_ = COMMUNICATION_FAILED; @@ -69,7 +69,7 @@ void QMC5883LComponent::dump_config() { ESP_LOGCONFIG(TAG, "QMC5883L:"); LOG_I2C_DEVICE(this); if (this->error_code_ == COMMUNICATION_FAILED) { - ESP_LOGE(TAG, "Communication with QMC5883L failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/qmp6988/qmp6988.cpp b/esphome/components/qmp6988/qmp6988.cpp index c24780eb25..4c81e124ba 100644 --- a/esphome/components/qmp6988/qmp6988.cpp +++ b/esphome/components/qmp6988/qmp6988.cpp @@ -348,7 +348,7 @@ void QMP6988Component::calculate_pressure_() { } void QMP6988Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up QMP6988"); + ESP_LOGCONFIG(TAG, "Running setup"); bool ret; ret = this->device_check_(); @@ -368,7 +368,7 @@ void QMP6988Component::dump_config() { ESP_LOGCONFIG(TAG, "QMP6988:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with QMP6988 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/qmp6988/qmp6988.h b/esphome/components/qmp6988/qmp6988.h index f0c11adf43..61b46a4189 100644 --- a/esphome/components/qmp6988/qmp6988.h +++ b/esphome/components/qmp6988/qmp6988.h @@ -1,9 +1,9 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" diff --git a/esphome/components/qr_code/qr_code.cpp b/esphome/components/qr_code/qr_code.cpp index b60e60a4b0..c2db741e17 100644 --- a/esphome/components/qr_code/qr_code.cpp +++ b/esphome/components/qr_code/qr_code.cpp @@ -9,8 +9,10 @@ namespace qr_code { static const char *const TAG = "qr_code"; void QrCode::dump_config() { - ESP_LOGCONFIG(TAG, "QR code:"); - ESP_LOGCONFIG(TAG, " Value: '%s'", this->value_.c_str()); + ESP_LOGCONFIG(TAG, + "QR code:\n" + " Value: '%s'", + this->value_.c_str()); } void QrCode::set_value(const std::string &value) { @@ -24,7 +26,7 @@ void QrCode::set_ecc(qrcodegen_Ecc ecc) { } void QrCode::generate_qr_code() { - ESP_LOGV(TAG, "Generating QR code..."); + ESP_LOGV(TAG, "Generating QR code"); uint8_t tempbuffer[qrcodegen_BUFFER_LEN_MAX]; if (!qrcodegen_encodeText(this->value_.c_str(), tempbuffer, this->qr_, this->ecc_, qrcodegen_VERSION_MIN, diff --git a/esphome/components/qspi_dbi/qspi_dbi.cpp b/esphome/components/qspi_dbi/qspi_dbi.cpp index fda6300d16..2901d40268 100644 --- a/esphome/components/qspi_dbi/qspi_dbi.cpp +++ b/esphome/components/qspi_dbi/qspi_dbi.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace qspi_dbi { void QspiDbi::setup() { - ESP_LOGCONFIG(TAG, "Setting up QSPI_DBI"); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); if (this->enable_pin_ != nullptr) { this->enable_pin_->setup(); diff --git a/esphome/components/qwiic_pir/qwiic_pir.cpp b/esphome/components/qwiic_pir/qwiic_pir.cpp index c267554c45..6a5196f831 100644 --- a/esphome/components/qwiic_pir/qwiic_pir.cpp +++ b/esphome/components/qwiic_pir/qwiic_pir.cpp @@ -7,35 +7,28 @@ namespace qwiic_pir { static const char *const TAG = "qwiic_pir"; void QwiicPIRComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up Qwiic PIR..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Verify I2C communcation by reading and verifying the chip ID uint8_t chip_id; - if (!this->read_byte(QWIIC_PIR_CHIP_ID, &chip_id)) { - ESP_LOGE(TAG, "Failed to read the chip's ID"); - + ESP_LOGE(TAG, "Failed to read chip ID"); this->error_code_ = ERROR_COMMUNICATION_FAILED; this->mark_failed(); - return; } if (chip_id != QWIIC_PIR_DEVICE_ID) { - ESP_LOGE(TAG, "Unknown chip ID, is this a Qwiic PIR?"); - + ESP_LOGE(TAG, "Unknown chip ID"); this->error_code_ = ERROR_WRONG_CHIP_ID; this->mark_failed(); - return; } if (!this->write_byte_16(QWIIC_PIR_DEBOUNCE_TIME, this->debounce_time_)) { - ESP_LOGE(TAG, "Failed to configure debounce time."); - + ESP_LOGE(TAG, "Failed to configure debounce time"); this->error_code_ = ERROR_COMMUNICATION_FAILED; this->mark_failed(); - return; } @@ -43,11 +36,9 @@ void QwiicPIRComponent::setup() { // Publish the starting raw state of the PIR sensor // If NATIVE mode, the binary_sensor state would be unknown until a motion event if (!this->read_byte(QWIIC_PIR_EVENT_STATUS, &this->event_register_.reg)) { - ESP_LOGE(TAG, "Failed to read initial sensor state."); - + ESP_LOGE(TAG, "Failed to read initial state"); this->error_code_ = ERROR_COMMUNICATION_FAILED; this->mark_failed(); - return; } @@ -58,8 +49,7 @@ void QwiicPIRComponent::setup() { void QwiicPIRComponent::loop() { // Read Event Register if (!this->read_byte(QWIIC_PIR_EVENT_STATUS, &this->event_register_.reg)) { - ESP_LOGW(TAG, "Failed to communicate with sensor"); - + ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); return; } @@ -98,39 +88,47 @@ void QwiicPIRComponent::loop() { } void QwiicPIRComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Qwiic PIR:"); + static const char *const RAW = "RAW"; + static const char *const NATIVE = "NATIVE"; + static const char *const HYBRID = "HYBRID"; - if (this->debounce_mode_ == RAW_DEBOUNCE_MODE) { - ESP_LOGCONFIG(TAG, " Debounce Mode: RAW"); - } else if (this->debounce_mode_ == NATIVE_DEBOUNCE_MODE) { - ESP_LOGCONFIG(TAG, " Debounce Mode: NATIVE"); - ESP_LOGCONFIG(TAG, " Debounce Time: %ums", this->debounce_time_); + const char *debounce_mode_str = RAW; + if (this->debounce_mode_ == NATIVE_DEBOUNCE_MODE) { + debounce_mode_str = NATIVE; } else if (this->debounce_mode_ == HYBRID_DEBOUNCE_MODE) { - ESP_LOGCONFIG(TAG, " Debounce Mode: HYBRID"); + debounce_mode_str = HYBRID; + } + + ESP_LOGCONFIG(TAG, + "Qwiic PIR:\n" + " Debounce Mode: %s", + debounce_mode_str); + if (this->debounce_mode_ == NATIVE_DEBOUNCE_MODE) { + ESP_LOGCONFIG(TAG, " Debounce Time: %ums", this->debounce_time_); } switch (this->error_code_) { case NONE: break; case ERROR_COMMUNICATION_FAILED: - ESP_LOGE(TAG, " Communication with Qwiic PIR failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case ERROR_WRONG_CHIP_ID: - ESP_LOGE(TAG, " Qwiic PIR has wrong chip ID - please verify you are using a Qwiic PIR"); + ESP_LOGE(TAG, "Unknown chip ID"); break; default: - ESP_LOGE(TAG, " Qwiic PIR error code %d", (int) this->error_code_); + ESP_LOGE(TAG, "Error %d", (int) this->error_code_); break; } LOG_I2C_DEVICE(this); - LOG_BINARY_SENSOR(" ", "Qwiic PIR Binary Sensor", this); + LOG_BINARY_SENSOR(" ", "Binary Sensor", this); } void QwiicPIRComponent::clear_events_() { // Clear event status register if (!this->write_byte(QWIIC_PIR_EVENT_STATUS, 0x00)) - ESP_LOGW(TAG, "Failed to clear events on sensor"); + ESP_LOGW(TAG, "Failed to clear events"); } } // namespace qwiic_pir diff --git a/esphome/components/rc522/rc522.cpp b/esphome/components/rc522/rc522.cpp index e2146dd14e..018b714190 100644 --- a/esphome/components/rc522/rc522.cpp +++ b/esphome/components/rc522/rc522.cpp @@ -46,7 +46,7 @@ void RC522::setup() { reset_pin_->pin_mode(gpio::FLAG_INPUT); if (!reset_pin_->digital_read()) { // The MFRC522 chip is in power down mode. - ESP_LOGV(TAG, "Power down mode detected. Hard resetting..."); + ESP_LOGV(TAG, "Power down mode detected. Hard resetting"); reset_pin_->pin_mode(gpio::FLAG_OUTPUT); // Now set the resetPowerDownPin as digital output. reset_pin_->digital_write(false); // Make sure we have a clean LOW state. delayMicroseconds(2); // 8.8.1 Reset timing requirements says about 100ns. Let us be generous: 2μsl @@ -101,7 +101,7 @@ void RC522::dump_config() { case NONE: break; case RESET_FAILED: - ESP_LOGE(TAG, "Reset command failed!"); + ESP_LOGE(TAG, "Reset command failed"); break; } @@ -292,7 +292,7 @@ void RC522::pcd_reset_() { return; if (reset_count_ == RESET_COUNT) { - ESP_LOGI(TAG, "Soft reset..."); + ESP_LOGI(TAG, "Soft reset"); // Issue the SoftReset command. pcd_write_register(COMMAND_REG, PCD_SOFT_RESET); } @@ -300,14 +300,14 @@ void RC522::pcd_reset_() { // Expect the PowerDown bit in CommandReg to be cleared (max 3x50ms) if ((pcd_read_register(COMMAND_REG) & (1 << 4)) == 0) { reset_count_ = 0; - ESP_LOGI(TAG, "Device online."); + ESP_LOGI(TAG, "Device online"); // Wait for initialize reset_timeout_ = millis(); return; } if (--reset_count_ == 0) { - ESP_LOGE(TAG, "Unable to reset RC522."); + ESP_LOGE(TAG, "Unable to reset"); this->error_code_ = RESET_FAILED; mark_failed(); } diff --git a/esphome/components/rdm6300/rdm6300.cpp b/esphome/components/rdm6300/rdm6300.cpp index bfdd880079..f9b6126c4b 100644 --- a/esphome/components/rdm6300/rdm6300.cpp +++ b/esphome/components/rdm6300/rdm6300.cpp @@ -1,4 +1,5 @@ #include "rdm6300.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index 8c21cb210c..6994eebd91 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -1,6 +1,6 @@ from esphome import pins import esphome.codegen as cg -from esphome.components import esp32_rmt, remote_base +from esphome.components import esp32, esp32_rmt, remote_base import esphome.config_validation as cv from esphome.const import ( CONF_BUFFER_SIZE, @@ -128,7 +128,9 @@ CONFIG_SCHEMA = remote_base.validate_triggers( esp32_idf=192, esp32_s2_idf=192, esp32_s3_idf=192, + esp32_p4_idf=192, esp32_c3_idf=96, + esp32_c5_idf=96, esp32_c6_idf=96, esp32_h2_idf=96, ): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)), @@ -139,7 +141,13 @@ CONFIG_SCHEMA = remote_base.validate_triggers( CONF_RECEIVE_SYMBOLS, esp32_idf=192, ): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)), - cv.Optional(CONF_USE_DMA): cv.All(cv.only_with_esp_idf, cv.boolean), + cv.Optional(CONF_USE_DMA): cv.All( + esp32.only_on_variant( + supported=[esp32.const.VARIANT_ESP32S3, esp32.const.VARIANT_ESP32P4] + ), + cv.only_with_esp_idf, + cv.boolean, + ), } ).extend(cv.COMPONENT_SCHEMA) ) diff --git a/esphome/components/remote_receiver/remote_receiver_esp32.cpp b/esphome/components/remote_receiver/remote_receiver_esp32.cpp index a8ee186d70..b78928d857 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp32.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp32.cpp @@ -40,7 +40,7 @@ static bool IRAM_ATTR HOT rmt_callback(rmt_channel_handle_t channel, const rmt_r #endif void RemoteReceiverComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up Remote Receiver..."); + ESP_LOGCONFIG(TAG, "Running setup"); #if ESP_IDF_VERSION_MAJOR >= 5 rmt_rx_channel_config_t channel; memset(&channel, 0, sizeof(channel)); @@ -161,23 +161,33 @@ void RemoteReceiverComponent::dump_config() { ESP_LOGCONFIG(TAG, "Remote Receiver:"); LOG_PIN(" Pin: ", this->pin_); #if ESP_IDF_VERSION_MAJOR >= 5 - ESP_LOGCONFIG(TAG, " Clock resolution: %" PRIu32 " hz", this->clock_resolution_); - ESP_LOGCONFIG(TAG, " RMT symbols: %" PRIu32, this->rmt_symbols_); - ESP_LOGCONFIG(TAG, " Filter symbols: %" PRIu32, this->filter_symbols_); - ESP_LOGCONFIG(TAG, " Receive symbols: %" PRIu32, this->receive_symbols_); + ESP_LOGCONFIG(TAG, + " Clock resolution: %" PRIu32 " hz\n" + " RMT symbols: %" PRIu32 "\n" + " Filter symbols: %" PRIu32 "\n" + " Receive symbols: %" PRIu32 "\n" + " Tolerance: %" PRIu32 "%s\n" + " Filter out pulses shorter than: %" PRIu32 " us\n" + " Signal is done after %" PRIu32 " us of no changes", + this->clock_resolution_, this->rmt_symbols_, this->filter_symbols_, this->receive_symbols_, + this->tolerance_, (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%", + this->filter_us_, this->idle_us_); #else if (this->pin_->digital_read()) { ESP_LOGW(TAG, "Remote Receiver Signal starts with a HIGH value. Usually this means you have to " "invert the signal using 'inverted: True' in the pin schema!"); } - ESP_LOGCONFIG(TAG, " Channel: %d", this->channel_); - ESP_LOGCONFIG(TAG, " RMT memory blocks: %d", this->mem_block_num_); - ESP_LOGCONFIG(TAG, " Clock divider: %u", this->clock_divider_); + ESP_LOGCONFIG(TAG, + " Channel: %d\n" + " RMT memory blocks: %d\n" + " Clock divider: %u\n" + " Tolerance: %" PRIu32 "%s\n" + " Filter out pulses shorter than: %" PRIu32 " us\n" + " Signal is done after %" PRIu32 " us of no changes", + this->channel_, this->mem_block_num_, this->clock_divider_, this->tolerance_, + (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%", this->filter_us_, + this->idle_us_); #endif - ESP_LOGCONFIG(TAG, " Tolerance: %" PRIu32 "%s", this->tolerance_, - (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%"); - ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %" PRIu32 " us", this->filter_us_); - ESP_LOGCONFIG(TAG, " Signal is done after %" PRIu32 " us of no changes", this->idle_us_); if (this->is_failed()) { ESP_LOGE(TAG, "Configuring RMT driver failed: %s (%s)", esp_err_to_name(this->error_code_), this->error_string_.c_str()); diff --git a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp index c92a134bd8..a0fd56bcf4 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp @@ -1,7 +1,7 @@ #include "remote_receiver.h" #include "esphome/core/hal.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_ESP8266 @@ -31,7 +31,7 @@ void IRAM_ATTR HOT RemoteReceiverComponentStore::gpio_intr(RemoteReceiverCompone } void RemoteReceiverComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up Remote Receiver..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->pin_->setup(); auto &s = this->store_; s.filter_us = this->filter_us_; @@ -63,11 +63,14 @@ void RemoteReceiverComponent::dump_config() { ESP_LOGW(TAG, "Remote Receiver Signal starts with a HIGH value. Usually this means you have to " "invert the signal using 'inverted: True' in the pin schema!"); } - ESP_LOGCONFIG(TAG, " Buffer Size: %u", this->buffer_size_); - ESP_LOGCONFIG(TAG, " Tolerance: %u%s", this->tolerance_, - (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%"); - ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %u us", this->filter_us_); - ESP_LOGCONFIG(TAG, " Signal is done after %u us of no changes", this->idle_us_); + ESP_LOGCONFIG(TAG, + " Buffer Size: %u\n" + " Tolerance: %u%s\n" + " Filter out pulses shorter than: %u us\n" + " Signal is done after %u us of no changes", + this->buffer_size_, this->tolerance_, + (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%", this->filter_us_, + this->idle_us_); } void RemoteReceiverComponent::loop() { diff --git a/esphome/components/remote_receiver/remote_receiver_libretiny.cpp b/esphome/components/remote_receiver/remote_receiver_libretiny.cpp index bfc29b4211..7a6054737e 100644 --- a/esphome/components/remote_receiver/remote_receiver_libretiny.cpp +++ b/esphome/components/remote_receiver/remote_receiver_libretiny.cpp @@ -1,7 +1,7 @@ #include "remote_receiver.h" #include "esphome/core/hal.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_LIBRETINY @@ -31,7 +31,7 @@ void IRAM_ATTR HOT RemoteReceiverComponentStore::gpio_intr(RemoteReceiverCompone } void RemoteReceiverComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up Remote Receiver..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->pin_->setup(); auto &s = this->store_; s.filter_us = this->filter_us_; @@ -63,11 +63,14 @@ void RemoteReceiverComponent::dump_config() { ESP_LOGW(TAG, "Remote Receiver Signal starts with a HIGH value. Usually this means you have to " "invert the signal using 'inverted: True' in the pin schema!"); } - ESP_LOGCONFIG(TAG, " Buffer Size: %u", this->buffer_size_); - ESP_LOGCONFIG(TAG, " Tolerance: %u%s", this->tolerance_, - (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%"); - ESP_LOGCONFIG(TAG, " Filter out pulses shorter than: %u us", this->filter_us_); - ESP_LOGCONFIG(TAG, " Signal is done after %u us of no changes", this->idle_us_); + ESP_LOGCONFIG(TAG, + " Buffer Size: %u\n" + " Tolerance: %u%s\n" + " Filter out pulses shorter than: %u us\n" + " Signal is done after %u us of no changes", + this->buffer_size_, this->tolerance_, + (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%", this->filter_us_, + this->idle_us_); } void RemoteReceiverComponent::loop() { diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index e7a94c175e..4db24760d8 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -1,6 +1,6 @@ from esphome import automation, pins import esphome.codegen as cg -from esphome.components import esp32_rmt, remote_base +from esphome.components import esp32, esp32_rmt, remote_base import esphome.config_validation as cv from esphome.const import ( CONF_CARRIER_DUTY_PERCENT, @@ -45,13 +45,21 @@ CONFIG_SCHEMA = cv.Schema( cv.only_on_esp32, cv.only_with_arduino, cv.int_range(min=1, max=255) ), cv.Optional(CONF_EOT_LEVEL): cv.All(cv.only_with_esp_idf, cv.boolean), - cv.Optional(CONF_USE_DMA): cv.All(cv.only_with_esp_idf, cv.boolean), + cv.Optional(CONF_USE_DMA): cv.All( + esp32.only_on_variant( + supported=[esp32.const.VARIANT_ESP32S3, esp32.const.VARIANT_ESP32P4] + ), + cv.only_with_esp_idf, + cv.boolean, + ), cv.SplitDefault( CONF_RMT_SYMBOLS, esp32_idf=64, esp32_s2_idf=64, esp32_s3_idf=48, + esp32_p4_idf=48, esp32_c3_idf=48, + esp32_c5_idf=48, esp32_c6_idf=48, esp32_h2_idf=48, ): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)), diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index 01a3980673..d51c45c607 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -11,7 +11,7 @@ namespace remote_transmitter { static const char *const TAG = "remote_transmitter"; void RemoteTransmitterComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up Remote Transmitter..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->inverted_ = this->pin_->is_inverted(); this->configure_rmt_(); } @@ -19,12 +19,16 @@ void RemoteTransmitterComponent::setup() { void RemoteTransmitterComponent::dump_config() { ESP_LOGCONFIG(TAG, "Remote Transmitter:"); #if ESP_IDF_VERSION_MAJOR >= 5 - ESP_LOGCONFIG(TAG, " Clock resolution: %" PRIu32 " hz", this->clock_resolution_); - ESP_LOGCONFIG(TAG, " RMT symbols: %" PRIu32, this->rmt_symbols_); + ESP_LOGCONFIG(TAG, + " Clock resolution: %" PRIu32 " hz\n" + " RMT symbols: %" PRIu32, + this->clock_resolution_, this->rmt_symbols_); #else - ESP_LOGCONFIG(TAG, " Channel: %d", this->channel_); - ESP_LOGCONFIG(TAG, " RMT memory blocks: %d", this->mem_block_num_); - ESP_LOGCONFIG(TAG, " Clock divider: %u", this->clock_divider_); + ESP_LOGCONFIG(TAG, + " Channel: %d\n" + " RMT memory blocks: %d\n" + " Clock divider: %u", + this->channel_, this->mem_block_num_, this->clock_divider_); #endif LOG_PIN(" Pin: ", this->pin_); diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp index 09cc16e975..73a1a7754f 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp @@ -15,8 +15,10 @@ void RemoteTransmitterComponent::setup() { } void RemoteTransmitterComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Remote Transmitter..."); - ESP_LOGCONFIG(TAG, " Carrier Duty: %u%%", this->carrier_duty_percent_); + ESP_LOGCONFIG(TAG, + "Remote Transmitter:\n" + " Carrier Duty: %u%%", + this->carrier_duty_percent_); LOG_PIN(" Pin: ", this->pin_); } @@ -72,7 +74,7 @@ void RemoteTransmitterComponent::space_(uint32_t usec) { } void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { - ESP_LOGD(TAG, "Sending remote code..."); + ESP_LOGD(TAG, "Sending remote code"); uint32_t on_time, off_time; this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time); this->target_time_ = 0; diff --git a/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp b/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp index 20d8736c00..42bf5bd95b 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp @@ -15,8 +15,10 @@ void RemoteTransmitterComponent::setup() { } void RemoteTransmitterComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Remote Transmitter..."); - ESP_LOGCONFIG(TAG, " Carrier Duty: %u%%", this->carrier_duty_percent_); + ESP_LOGCONFIG(TAG, + "Remote Transmitter:\n" + " Carrier Duty: %u%%", + this->carrier_duty_percent_); LOG_PIN(" Pin: ", this->pin_); } @@ -74,7 +76,7 @@ void RemoteTransmitterComponent::space_(uint32_t usec) { } void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { - ESP_LOGD(TAG, "Sending remote code..."); + ESP_LOGD(TAG, "Sending remote code"); uint32_t on_time, off_time; this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time); this->target_time_ = 0; diff --git a/esphome/components/resistance/resistance_sensor.cpp b/esphome/components/resistance/resistance_sensor.cpp index e13959fd37..6e57214449 100644 --- a/esphome/components/resistance/resistance_sensor.cpp +++ b/esphome/components/resistance/resistance_sensor.cpp @@ -8,9 +8,12 @@ static const char *const TAG = "resistance"; void ResistanceSensor::dump_config() { LOG_SENSOR("", "Resistance Sensor", this); - ESP_LOGCONFIG(TAG, " Configuration: %s", this->configuration_ == UPSTREAM ? "UPSTREAM" : "DOWNSTREAM"); - ESP_LOGCONFIG(TAG, " Resistor: %.2fΩ", this->resistor_); - ESP_LOGCONFIG(TAG, " Reference Voltage: %.1fV", this->reference_voltage_); + ESP_LOGCONFIG(TAG, + " Configuration: %s\n" + " Resistor: %.2fΩ\n" + " Reference Voltage: %.1fV", + this->configuration_ == UPSTREAM ? "UPSTREAM" : "DOWNSTREAM", this->resistor_, + this->reference_voltage_); } void ResistanceSensor::process_(float value) { if (std::isnan(value)) { diff --git a/esphome/components/restart/button/restart_button.cpp b/esphome/components/restart/button/restart_button.cpp index d8ff061355..accb1a8356 100644 --- a/esphome/components/restart/button/restart_button.cpp +++ b/esphome/components/restart/button/restart_button.cpp @@ -9,7 +9,7 @@ namespace restart { static const char *const TAG = "restart.button"; void RestartButton::press_action() { - ESP_LOGI(TAG, "Restarting device..."); + ESP_LOGI(TAG, "Restarting device"); // Let MQTT settle a bit delay(100); // NOLINT App.safe_reboot(); diff --git a/esphome/components/restart/switch/restart_switch.cpp b/esphome/components/restart/switch/restart_switch.cpp index 3076fde99e..422e85f4cd 100644 --- a/esphome/components/restart/switch/restart_switch.cpp +++ b/esphome/components/restart/switch/restart_switch.cpp @@ -13,7 +13,7 @@ void RestartSwitch::write_state(bool state) { this->publish_state(false); if (state) { - ESP_LOGI(TAG, "Restarting device..."); + ESP_LOGI(TAG, "Restarting device"); // Let MQTT settle a bit delay(100); // NOLINT App.safe_reboot(); diff --git a/esphome/components/rotary_encoder/rotary_encoder.cpp b/esphome/components/rotary_encoder/rotary_encoder.cpp index f8e5357a6e..79bc123597 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.cpp +++ b/esphome/components/rotary_encoder/rotary_encoder.cpp @@ -1,6 +1,6 @@ #include "rotary_encoder.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace rotary_encoder { @@ -129,7 +129,7 @@ void IRAM_ATTR HOT RotaryEncoderSensorStore::gpio_intr(RotaryEncoderSensorStore } void RotaryEncoderSensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up Rotary Encoder '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); int32_t initial_value = 0; switch (this->restore_mode_) { diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index 3d73cad195..c3e11336a9 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -10,6 +10,7 @@ from esphome.const import ( CONF_PLATFORM_VERSION, CONF_SOURCE, CONF_VERSION, + CONF_WATCHDOG_TIMEOUT, KEY_CORE, KEY_FRAMEWORK_VERSION, KEY_TARGET_FRAMEWORK, @@ -147,6 +148,10 @@ CONFIG_SCHEMA = cv.All( { cv.Required(CONF_BOARD): cv.string_strict, cv.Optional(CONF_FRAMEWORK, default={}): ARDUINO_FRAMEWORK_SCHEMA, + cv.Optional(CONF_WATCHDOG_TIMEOUT, default="8388ms"): cv.All( + cv.positive_time_period_milliseconds, + cv.Range(max=cv.TimePeriod(milliseconds=8388)), + ), } ), set_core_data, @@ -189,6 +194,8 @@ async def to_code(config): cg.RawExpression(f"VERSION_CODE({ver.major}, {ver.minor}, {ver.patch})"), ) + cg.add_define("USE_RP2040_WATCHDOG_TIMEOUT", config[CONF_WATCHDOG_TIMEOUT]) + def add_pio_file(component: str, key: str, data: str): try: diff --git a/esphome/components/rp2040/core.cpp b/esphome/components/rp2040/core.cpp index c20401c791..d88e9f54b7 100644 --- a/esphome/components/rp2040/core.cpp +++ b/esphome/components/rp2040/core.cpp @@ -1,6 +1,7 @@ #ifdef USE_RP2040 #include "core.h" +#include "esphome/core/defines.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" @@ -19,7 +20,13 @@ void arch_restart() { continue; } } -void arch_init() { watchdog_enable(0x7fffff, false); } + +void arch_init() { +#if USE_RP2040_WATCHDOG_TIMEOUT > 0 + watchdog_enable(USE_RP2040_WATCHDOG_TIMEOUT, false); +#endif +} + void IRAM_ATTR HOT arch_feed_wdt() { watchdog_update(); } uint8_t progmem_read_byte(const uint8_t *addr) { diff --git a/esphome/components/rp2040/preferences.cpp b/esphome/components/rp2040/preferences.cpp index e7aa9ab28d..cbf2b00641 100644 --- a/esphome/components/rp2040/preferences.cpp +++ b/esphome/components/rp2040/preferences.cpp @@ -87,7 +87,7 @@ class RP2040Preferences : public ESPPreferences { RP2040Preferences() : eeprom_sector_(&_EEPROM_start) {} void setup() { s_flash_storage = new uint8_t[RP2040_FLASH_STORAGE_SIZE]; // NOLINT - ESP_LOGVV(TAG, "Loading preferences from flash..."); + ESP_LOGVV(TAG, "Loading preferences from flash"); memcpy(s_flash_storage, this->eeprom_sector_, RP2040_FLASH_STORAGE_SIZE); } @@ -114,7 +114,7 @@ class RP2040Preferences : public ESPPreferences { if (s_prevent_write) return false; - ESP_LOGD(TAG, "Saving preferences to flash..."); + ESP_LOGD(TAG, "Saving"); { InterruptLock lock; @@ -129,7 +129,7 @@ class RP2040Preferences : public ESPPreferences { } bool reset() override { - ESP_LOGD(TAG, "Cleaning up preferences in flash..."); + ESP_LOGD(TAG, "Erasing storage"); { InterruptLock lock; ::rp2040.idleOtherCore(); diff --git a/esphome/components/rp2040_pio_led_strip/led_strip.cpp b/esphome/components/rp2040_pio_led_strip/led_strip.cpp index 2aaa2ceb19..a6ff037d88 100644 --- a/esphome/components/rp2040_pio_led_strip/led_strip.cpp +++ b/esphome/components/rp2040_pio_led_strip/led_strip.cpp @@ -40,7 +40,7 @@ void RP2040PIOLEDStripLightOutput::dma_write_complete_handler_() { } void RP2040PIOLEDStripLightOutput::setup() { - ESP_LOGCONFIG(TAG, "Setting up RP2040 LED Strip..."); + ESP_LOGCONFIG(TAG, "Running setup"); size_t buffer_size = this->get_buffer_size_(); @@ -138,7 +138,7 @@ void RP2040PIOLEDStripLightOutput::setup() { } void RP2040PIOLEDStripLightOutput::write_state(light::LightState *state) { - ESP_LOGVV(TAG, "Writing state..."); + ESP_LOGVV(TAG, "Writing state"); if (this->is_failed()) { ESP_LOGW(TAG, "Light is in failed state, not writing state."); @@ -199,12 +199,15 @@ light::ESPColorView RP2040PIOLEDStripLightOutput::get_view_internal(int32_t inde } void RP2040PIOLEDStripLightOutput::dump_config() { - ESP_LOGCONFIG(TAG, "RP2040 PIO LED Strip Light Output:"); - ESP_LOGCONFIG(TAG, " Pin: GPIO%d", this->pin_); - ESP_LOGCONFIG(TAG, " Number of LEDs: %d", this->num_leds_); - ESP_LOGCONFIG(TAG, " RGBW: %s", YESNO(this->is_rgbw_)); - ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order_to_string(this->rgb_order_)); - ESP_LOGCONFIG(TAG, " Max Refresh Rate: %f Hz", this->max_refresh_rate_); + ESP_LOGCONFIG(TAG, + "RP2040 PIO LED Strip Light Output:\n" + " Pin: GPIO%d\n" + " Number of LEDs: %d\n" + " RGBW: %s\n" + " RGB Order: %s\n" + " Max Refresh Rate: %f Hz", + this->pin_, this->num_leds_, YESNO(this->is_rgbw_), rgb_order_to_string(this->rgb_order_), + this->max_refresh_rate_); } float RP2040PIOLEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; } diff --git a/esphome/components/rp2040_pio_led_strip/light.py b/esphome/components/rp2040_pio_led_strip/light.py index 4b6a80e78b..9107db9b7f 100644 --- a/esphome/components/rp2040_pio_led_strip/light.py +++ b/esphome/components/rp2040_pio_led_strip/light.py @@ -173,7 +173,7 @@ RGB_ORDERS = { CHIPSET_TIMINGS = { "WS2812": LEDStripTimings(20, 40, 46, 34), "WS2812B": LEDStripTimings(23, 49, 46, 26), - "SK6812": LEDStripTimings(17, 52, 34, 34), + "SK6812": LEDStripTimings(20, 54, 38, 38), "SM16703": LEDStripTimings(17, 52, 52, 17), } diff --git a/esphome/components/rp2040_pwm/rp2040_pwm.cpp b/esphome/components/rp2040_pwm/rp2040_pwm.cpp index 170af59905..40920f9351 100644 --- a/esphome/components/rp2040_pwm/rp2040_pwm.cpp +++ b/esphome/components/rp2040_pwm/rp2040_pwm.cpp @@ -17,7 +17,7 @@ namespace rp2040_pwm { static const char *const TAG = "rp2040_pwm"; void RP2040PWM::setup() { - ESP_LOGCONFIG(TAG, "Setting up RP2040 PWM Output..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->setup_pwm_(); } diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp index ba09171649..666bac354d 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace rpi_dpi_rgb { void RpiDpiRgb::setup() { - ESP_LOGCONFIG(TAG, "Setting up RPI_DPI_RGB"); + ESP_LOGCONFIG(TAG, "Running setup"); this->reset_display_(); esp_lcd_rgb_panel_config_t config{}; config.flags.fb_in_psram = 1; diff --git a/esphome/components/rtttl/rtttl.cpp b/esphome/components/rtttl/rtttl.cpp index db4cc731e4..e24816fd83 100644 --- a/esphome/components/rtttl/rtttl.cpp +++ b/esphome/components/rtttl/rtttl.cpp @@ -27,15 +27,17 @@ inline double deg2rad(double degrees) { } void Rtttl::dump_config() { - ESP_LOGCONFIG(TAG, "Rtttl:"); - ESP_LOGCONFIG(TAG, " Gain: %f", gain_); + ESP_LOGCONFIG(TAG, + "Rtttl:\n" + " Gain: %f", + this->gain_); } void Rtttl::play(std::string rtttl) { if (this->state_ != State::STATE_STOPPED && this->state_ != State::STATE_STOPPING) { int pos = this->rtttl_.find(':'); auto name = this->rtttl_.substr(0, pos); - ESP_LOGW(TAG, "RTTTL Component is already playing: %s", name.c_str()); + ESP_LOGW(TAG, "Already playing: %s", name.c_str()); return; } @@ -49,11 +51,11 @@ void Rtttl::play(std::string rtttl) { uint8_t num; // Get name - this->position_ = rtttl_.find(':'); + this->position_ = this->rtttl_.find(':'); // it's somewhat documented to be up to 10 characters but let's be a bit flexible here if (this->position_ == std::string::npos || this->position_ > 15) { - ESP_LOGE(TAG, "Missing ':' when looking for name."); + ESP_LOGE(TAG, "Unable to determine name; missing ':'"); return; } @@ -203,7 +205,7 @@ void Rtttl::loop() { if (this->output_ != nullptr && millis() - this->last_note_ < this->note_duration_) return; #endif - if (!this->rtttl_[position_]) { + if (!this->rtttl_[this->position_]) { this->finish_(); return; } @@ -271,7 +273,7 @@ void Rtttl::loop() { scale = this->default_octave_; if (scale < 4 || scale > 7) { - ESP_LOGE(TAG, "Octave out of valid range. Should be between 4 and 7. (Octave: %d)", scale); + ESP_LOGE(TAG, "Octave must be between 4 and 7 (it is %d)", scale); this->finish_(); return; } @@ -281,7 +283,7 @@ void Rtttl::loop() { if (note) { auto note_index = (scale - 4) * 12 + note; if (note_index < 0 || note_index >= (int) sizeof(NOTES)) { - ESP_LOGE(TAG, "Note out of valid range (note: %d, scale: %d, index: %d, max: %d)", note, scale, note_index, + ESP_LOGE(TAG, "Note out of range (note: %d, scale: %d, index: %d, max: %d)", note, scale, note_index, (int) sizeof(NOTES)); this->finish_(); return; @@ -387,7 +389,7 @@ static const LogString *state_to_string(State state) { void Rtttl::set_state_(State state) { State old_state = this->state_; this->state_ = state; - ESP_LOGD(TAG, "State changed from %s to %s", LOG_STR_ARG(state_to_string(old_state)), + ESP_LOGV(TAG, "State changed from %s to %s", LOG_STR_ARG(state_to_string(old_state)), LOG_STR_ARG(state_to_string(state))); } diff --git a/esphome/components/safe_mode/button/safe_mode_button.cpp b/esphome/components/safe_mode/button/safe_mode_button.cpp index 261688807a..bb5b64daf7 100644 --- a/esphome/components/safe_mode/button/safe_mode_button.cpp +++ b/esphome/components/safe_mode/button/safe_mode_button.cpp @@ -13,7 +13,7 @@ void SafeModeButton::set_safe_mode(SafeModeComponent *safe_mode_component) { } void SafeModeButton::press_action() { - ESP_LOGI(TAG, "Restarting device in safe mode..."); + ESP_LOGI(TAG, "Restarting in safe mode"); this->safe_mode_component_->set_safe_mode_pending(true); // Let MQTT settle a bit diff --git a/esphome/components/safe_mode/safe_mode.cpp b/esphome/components/safe_mode/safe_mode.cpp index aa1a4b6822..89c9242357 100644 --- a/esphome/components/safe_mode/safe_mode.cpp +++ b/esphome/components/safe_mode/safe_mode.cpp @@ -16,17 +16,18 @@ static const char *const TAG = "safe_mode"; void SafeModeComponent::dump_config() { ESP_LOGCONFIG(TAG, "Safe Mode:"); - ESP_LOGCONFIG(TAG, " Boot considered successful after %" PRIu32 " seconds", - this->safe_mode_boot_is_good_after_ / 1000); // because milliseconds - ESP_LOGCONFIG(TAG, " Invoke after %u boot attempts", this->safe_mode_num_attempts_); - ESP_LOGCONFIG(TAG, " Remain in safe mode for %" PRIu32 " seconds", + ESP_LOGCONFIG(TAG, + " Boot considered successful after %" PRIu32 " seconds\n" + " Invoke after %u boot attempts\n" + " Remain for %" PRIu32 " seconds", + this->safe_mode_boot_is_good_after_ / 1000, // because milliseconds + this->safe_mode_num_attempts_, this->safe_mode_enable_time_ / 1000); // because milliseconds if (this->safe_mode_rtc_value_ > 1 && this->safe_mode_rtc_value_ != SafeModeComponent::ENTER_SAFE_MODE_MAGIC) { auto remaining_restarts = this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_; if (remaining_restarts) { - ESP_LOGW(TAG, "Last reset occurred too quickly; safe mode will be invoked in %" PRIu32 " restarts", - remaining_restarts); + ESP_LOGW(TAG, "Last reset occurred too quickly; will be invoked in %" PRIu32 " restarts", remaining_restarts); } else { ESP_LOGW(TAG, "SAFE MODE IS ACTIVE"); } @@ -48,7 +49,7 @@ void SafeModeComponent::set_safe_mode_pending(const bool &pending) { uint32_t current_rtc = this->read_rtc_(); if (pending && current_rtc != SafeModeComponent::ENTER_SAFE_MODE_MAGIC) { - ESP_LOGI(TAG, "Device will enter safe mode on next boot"); + ESP_LOGI(TAG, "Device will enter on next boot"); this->write_rtc_(SafeModeComponent::ENTER_SAFE_MODE_MAGIC); } @@ -83,7 +84,7 @@ bool SafeModeComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t en this->clean_rtc(); if (!is_manual_safe_mode) { - ESP_LOGE(TAG, "Boot loop detected. Proceeding to safe mode"); + ESP_LOGE(TAG, "Boot loop detected. Proceeding"); } this->status_set_error(); diff --git a/esphome/components/safe_mode/switch/safe_mode_switch.cpp b/esphome/components/safe_mode/switch/safe_mode_switch.cpp index 13b35ed210..1637da3059 100644 --- a/esphome/components/safe_mode/switch/safe_mode_switch.cpp +++ b/esphome/components/safe_mode/switch/safe_mode_switch.cpp @@ -17,7 +17,7 @@ void SafeModeSwitch::write_state(bool state) { this->publish_state(false); if (state) { - ESP_LOGI(TAG, "Restarting device in safe mode..."); + ESP_LOGI(TAG, "Restarting in safe mode"); this->safe_mode_component_->set_safe_mode_pending(true); // Let MQTT settle a bit diff --git a/esphome/components/scd30/scd30.cpp b/esphome/components/scd30/scd30.cpp index 3eeca23800..8561732d8b 100644 --- a/esphome/components/scd30/scd30.cpp +++ b/esphome/components/scd30/scd30.cpp @@ -26,7 +26,7 @@ static const uint16_t SCD30_CMD_TEMPERATURE_OFFSET = 0x5403; static const uint16_t SCD30_CMD_SOFT_RESET = 0xD304; void SCD30Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up scd30..."); + ESP_LOGCONFIG(TAG, "Running setup"); #ifdef USE_ESP8266 Wire.setClockStretchLimit(150000); @@ -122,16 +122,16 @@ void SCD30Component::dump_config() { if (this->is_failed()) { switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGW(TAG, "Communication failed! Is the sensor connected?"); + ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); break; case MEASUREMENT_INIT_FAILED: - ESP_LOGW(TAG, "Measurement Initialization failed!"); + ESP_LOGW(TAG, "Measurement Initialization failed"); break; case FIRMWARE_IDENTIFICATION_FAILED: ESP_LOGW(TAG, "Unable to read sensor firmware version"); break; default: - ESP_LOGW(TAG, "Unknown setup error!"); + ESP_LOGW(TAG, "Unknown setup error"); break; } } @@ -140,10 +140,13 @@ void SCD30Component::dump_config() { } else { ESP_LOGCONFIG(TAG, " Altitude compensation: %dm", this->altitude_compensation_); } - ESP_LOGCONFIG(TAG, " Automatic self calibration: %s", ONOFF(this->enable_asc_)); - ESP_LOGCONFIG(TAG, " Ambient pressure compensation: %dmBar", this->ambient_pressure_compensation_); - ESP_LOGCONFIG(TAG, " Temperature offset: %.2f °C", this->temperature_offset_); - ESP_LOGCONFIG(TAG, " Update interval: %ds", this->update_interval_); + ESP_LOGCONFIG(TAG, + " Automatic self calibration: %s\n" + " Ambient pressure compensation: %dmBar\n" + " Temperature offset: %.2f °C\n" + " Update interval: %ds", + ONOFF(this->enable_asc_), this->ambient_pressure_compensation_, this->temperature_offset_, + this->update_interval_); LOG_SENSOR(" ", "CO2", this->co2_sensor_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); diff --git a/esphome/components/scd4x/scd4x.cpp b/esphome/components/scd4x/scd4x.cpp index a8a4129b48..f617ffe276 100644 --- a/esphome/components/scd4x/scd4x.cpp +++ b/esphome/components/scd4x/scd4x.cpp @@ -27,7 +27,7 @@ static const uint16_t SCD41_ID = 0x1408; static const uint16_t SCD40_ID = 0x440; void SCD4XComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up scd4x..."); + ESP_LOGCONFIG(TAG, "Running setup"); // the sensor needs 1000 ms to enter the idle state this->set_timeout(1000, [this]() { this->status_clear_error(); @@ -96,16 +96,16 @@ void SCD4XComponent::dump_config() { if (this->is_failed()) { switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGW(TAG, "Communication failed! Is the sensor connected?"); + ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); break; case MEASUREMENT_INIT_FAILED: - ESP_LOGW(TAG, "Measurement Initialization failed!"); + ESP_LOGW(TAG, "Measurement Initialization failed"); break; case SERIAL_NUMBER_IDENTIFICATION_FAILED: ESP_LOGW(TAG, "Unable to read sensor firmware version"); break; default: - ESP_LOGW(TAG, "Unknown setup error!"); + ESP_LOGW(TAG, "Unknown setup error"); break; } } @@ -115,11 +115,15 @@ void SCD4XComponent::dump_config() { this->ambient_pressure_source_->get_name().c_str()); } else { if (this->ambient_pressure_compensation_) { - ESP_LOGCONFIG(TAG, " Altitude compensation disabled"); - ESP_LOGCONFIG(TAG, " Ambient pressure compensation: %dmBar", this->ambient_pressure_); + ESP_LOGCONFIG(TAG, + " Altitude compensation disabled\n" + " Ambient pressure compensation: %dmBar", + this->ambient_pressure_); } else { - ESP_LOGCONFIG(TAG, " Ambient pressure compensation disabled"); - ESP_LOGCONFIG(TAG, " Altitude compensation: %dm", this->altitude_compensation_); + ESP_LOGCONFIG(TAG, + " Ambient pressure compensation disabled\n" + " Altitude compensation: %dm", + this->altitude_compensation_); } } switch (this->measurement_mode_) { diff --git a/esphome/components/sdl/display.py b/esphome/components/sdl/display.py index 66709cc834..ae8b0fd43a 100644 --- a/esphome/components/sdl/display.py +++ b/esphome/components/sdl/display.py @@ -8,16 +8,28 @@ from esphome.const import ( CONF_HEIGHT, CONF_ID, CONF_LAMBDA, + CONF_POSITION, CONF_WIDTH, + CONF_X, + CONF_Y, PLATFORM_HOST, ) sdl_ns = cg.esphome_ns.namespace("sdl") Sdl = sdl_ns.class_("Sdl", display.Display, cg.Component) +sdl_window_flags = cg.global_ns.enum("SDL_WindowFlags") CONF_SDL_OPTIONS = "sdl_options" CONF_SDL_ID = "sdl_id" +CONF_WINDOW_OPTIONS = "window_options" +WINDOW_OPTIONS = ( + "borderless", + "always_on_top", + "fullscreen", + "skip_taskbar", + "resizable", +) def get_sdl_options(value): @@ -29,6 +41,10 @@ def get_sdl_options(value): raise cv.Invalid("Unable to run sdl2-config - have you installed sdl2?") from e +def get_window_options(): + return {cv.Optional(option, default=False): cv.boolean for option in WINDOW_OPTIONS} + + CONFIG_SCHEMA = cv.All( display.FULL_DISPLAY_SCHEMA.extend( cv.Schema( @@ -44,6 +60,17 @@ CONFIG_SCHEMA = cv.All( } ), ), + cv.Optional(CONF_WINDOW_OPTIONS): cv.Schema( + { + cv.Optional(CONF_POSITION): cv.Schema( + { + cv.Required(CONF_X): cv.int_, + cv.Required(CONF_Y): cv.int_, + } + ), + **get_window_options(), + } + ), } ) ), @@ -65,6 +92,19 @@ async def to_code(config): (width, height) = dimensions cg.add(var.set_dimensions(width, height)) + if window_options := config.get(CONF_WINDOW_OPTIONS): + create_flags = 0 + for option in WINDOW_OPTIONS: + value = window_options.get(option, False) + if value: + create_flags = create_flags | getattr( + sdl_window_flags, "SDL_WINDOW_" + option.upper() + ) + cg.add(var.set_window_options(create_flags)) + + if position := window_options.get(CONF_POSITION): + cg.add(var.set_position(position[CONF_X], position[CONF_Y])) + if lamb := config.get(CONF_LAMBDA): lambda_ = await cg.process_lambda( lamb, [(display.DisplayRef, "it")], return_type=cg.void diff --git a/esphome/components/sdl/sdl_esphome.cpp b/esphome/components/sdl/sdl_esphome.cpp index 42dfe687e9..e55bff58fe 100644 --- a/esphome/components/sdl/sdl_esphome.cpp +++ b/esphome/components/sdl/sdl_esphome.cpp @@ -8,8 +8,8 @@ namespace sdl { void Sdl::setup() { ESP_LOGD(TAG, "Starting setup"); SDL_Init(SDL_INIT_VIDEO); - this->window_ = SDL_CreateWindow(App.get_name().c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - this->width_, this->height_, SDL_WINDOW_RESIZABLE); + this->window_ = SDL_CreateWindow(App.get_name().c_str(), this->pos_x_, this->pos_y_, this->width_, this->height_, + this->window_options_); this->renderer_ = SDL_CreateRenderer(this->window_, -1, SDL_RENDERER_SOFTWARE); SDL_RenderSetLogicalSize(this->renderer_, this->width_, this->height_); this->texture_ = diff --git a/esphome/components/sdl/sdl_esphome.h b/esphome/components/sdl/sdl_esphome.h index 39ea3ed417..bf5fde1428 100644 --- a/esphome/components/sdl/sdl_esphome.h +++ b/esphome/components/sdl/sdl_esphome.h @@ -28,6 +28,11 @@ class Sdl : public display::Display { this->width_ = width; this->height_ = height; } + void set_window_options(uint32_t window_options) { this->window_options_ = window_options; } + void set_position(uint16_t pos_x, uint16_t pos_y) { + this->pos_x_ = pos_x; + this->pos_y_ = pos_y; + } int get_width() override { return this->width_; } int get_height() override { return this->height_; } float get_setup_priority() const override { return setup_priority::HARDWARE; } @@ -49,6 +54,9 @@ class Sdl : public display::Display { void redraw_(SDL_Rect &rect); int width_{}; int height_{}; + uint32_t window_options_{0}; + int pos_x_{SDL_WINDOWPOS_UNDEFINED}; + int pos_y_{SDL_WINDOWPOS_UNDEFINED}; SDL_Renderer *renderer_{}; SDL_Window *window_{}; SDL_Texture *texture_{}; diff --git a/esphome/components/sdm_meter/sdm_meter.cpp b/esphome/components/sdm_meter/sdm_meter.cpp index 18e06e2b04..12a3277269 100644 --- a/esphome/components/sdm_meter/sdm_meter.cpp +++ b/esphome/components/sdm_meter/sdm_meter.cpp @@ -1,5 +1,6 @@ #include "sdm_meter.h" #include "sdm_meter_registers.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -84,8 +85,10 @@ void SDMMeter::on_modbus_data(const std::vector &data) { void SDMMeter::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); } void SDMMeter::dump_config() { - ESP_LOGCONFIG(TAG, "SDM Meter:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "SDM Meter:\n" + " Address: 0x%02X", + this->address_); for (uint8_t i = 0; i < 3; i++) { auto phase = this->phases_[i]; if (!phase.setup) diff --git a/esphome/components/sdp3x/sdp3x.cpp b/esphome/components/sdp3x/sdp3x.cpp index 7a957e55ac..58aefe09d7 100644 --- a/esphome/components/sdp3x/sdp3x.cpp +++ b/esphome/components/sdp3x/sdp3x.cpp @@ -1,7 +1,7 @@ #include "sdp3x.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace sdp3x { @@ -17,31 +17,31 @@ static const uint16_t SDP3X_STOP_MEAS = 0x3FF9; void SDP3XComponent::update() { this->read_pressure_(); } void SDP3XComponent::setup() { - ESP_LOGD(TAG, "Setting up SDP3X..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->write_command(SDP3X_STOP_MEAS)) { - ESP_LOGW(TAG, "Stop SDP3X failed!"); // This sometimes fails for no good reason + ESP_LOGW(TAG, "Stop failed"); // This sometimes fails for no good reason } if (!this->write_command(SDP3X_SOFT_RESET)) { - ESP_LOGW(TAG, "Soft Reset SDP3X failed!"); // This sometimes fails for no good reason + ESP_LOGW(TAG, "Soft Reset failed"); // This sometimes fails for no good reason } this->set_timeout(20, [this] { if (!this->write_command(SDP3X_READ_ID1)) { - ESP_LOGE(TAG, "Read ID1 SDP3X failed!"); + ESP_LOGE(TAG, "Read ID1 failed"); this->mark_failed(); return; } if (!this->write_command(SDP3X_READ_ID2)) { - ESP_LOGE(TAG, "Read ID2 SDP3X failed!"); + ESP_LOGE(TAG, "Read ID2 failed"); this->mark_failed(); return; } uint16_t data[6]; if (!this->read_data(data, 6)) { - ESP_LOGE(TAG, "Read ID SDP3X failed!"); + ESP_LOGE(TAG, "Read ID failed"); this->mark_failed(); return; } @@ -79,18 +79,18 @@ void SDP3XComponent::setup() { } if (!this->write_command(measurement_mode_ == DP_AVG ? SDP3X_START_DP_AVG : SDP3X_START_MASS_FLOW_AVG)) { - ESP_LOGE(TAG, "Start Measurements SDP3X failed!"); + ESP_LOGE(TAG, "Start Measurements failed"); this->mark_failed(); return; } - ESP_LOGCONFIG(TAG, "SDP3X started!"); + ESP_LOGCONFIG(TAG, "started"); }); } void SDP3XComponent::dump_config() { LOG_SENSOR(" ", "SDP3X", this); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, " Connection with SDP3X failed!"); + ESP_LOGE(TAG, " Connection with failed"); } LOG_UPDATE_INTERVAL(this); } @@ -98,7 +98,7 @@ void SDP3XComponent::dump_config() { void SDP3XComponent::read_pressure_() { uint16_t data[3]; if (!this->read_data(data, 3)) { - ESP_LOGW(TAG, "Couldn't read SDP3X data!"); + ESP_LOGW(TAG, "Couldn't read data"); this->status_set_warning(); return; } diff --git a/esphome/components/sds011/sds011.cpp b/esphome/components/sds011/sds011.cpp index a34059d85d..4e12c0e322 100644 --- a/esphome/components/sds011/sds011.cpp +++ b/esphome/components/sds011/sds011.cpp @@ -67,9 +67,11 @@ void SDS011Component::set_working_state(bool working_state) { } void SDS011Component::dump_config() { - ESP_LOGCONFIG(TAG, "SDS011:"); - ESP_LOGCONFIG(TAG, " Update Interval: %u min", this->update_interval_min_); - ESP_LOGCONFIG(TAG, " RX-only mode: %s", ONOFF(this->rx_mode_only_)); + ESP_LOGCONFIG(TAG, + "SDS011:\n" + " Update Interval: %u min\n" + " RX-only mode: %s", + this->update_interval_min_, ONOFF(this->rx_mode_only_)); LOG_SENSOR(" ", "PM2.5", this->pm_2_5_sensor_); LOG_SENSOR(" ", "PM10.0", this->pm_10_0_sensor_); this->check_uart_settings(9600); @@ -180,9 +182,6 @@ void SDS011Component::parse_data_() { } } -uint16_t SDS011Component::get_16_bit_uint_(uint8_t start_index) const { - return (uint16_t(this->data_[start_index + 1]) << 8) | uint16_t(this->data_[start_index]); -} void SDS011Component::set_update_interval_min(uint8_t update_interval_min) { this->update_interval_min_ = update_interval_min; } diff --git a/esphome/components/sds011/sds011.h b/esphome/components/sds011/sds011.h index 19e0cd3efe..1f404601b1 100644 --- a/esphome/components/sds011/sds011.h +++ b/esphome/components/sds011/sds011.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/uart/uart.h" @@ -32,7 +33,9 @@ class SDS011Component : public Component, public uart::UARTDevice { uint8_t sds011_checksum_(const uint8_t *command_data, uint8_t length) const; optional check_byte_() const; void parse_data_(); - uint16_t get_16_bit_uint_(uint8_t start_index) const; + uint16_t get_16_bit_uint_(uint8_t start_index) const { + return encode_uint16(this->data_[start_index + 1], this->data_[start_index]); + } sensor::Sensor *pm_2_5_sensor_{nullptr}; sensor::Sensor *pm_10_0_sensor_{nullptr}; diff --git a/esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp b/esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp index 1cf9bd300a..8683d6cad7 100644 --- a/esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp +++ b/esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp @@ -1,5 +1,6 @@ #include "seeed_mr24hpc1.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" #include @@ -61,7 +62,7 @@ void MR24HPC1Component::dump_config() { // Initialisation functions void MR24HPC1Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MR24HPC1..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->check_uart_settings(115200); if (this->custom_mode_number_ != nullptr) { @@ -533,7 +534,7 @@ void MR24HPC1Component::r24_frame_parse_work_status_(uint8_t *data) { this->custom_mode_number_->publish_state(0); } if (this->custom_mode_end_text_sensor_ != nullptr) { - this->custom_mode_end_text_sensor_->publish_state("Setup in progress..."); + this->custom_mode_end_text_sensor_->publish_state("Setup in progress"); } } else if (data[FRAME_COMMAND_WORD_INDEX] == 0x81) { ESP_LOGD(TAG, "Reply: get radar init status 0x%02X", data[FRAME_DATA_INDEX]); diff --git a/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp index 75f3f092a6..c815c98419 100644 --- a/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +++ b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp @@ -1,4 +1,5 @@ #include "seeed_mr60bha2.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" #include diff --git a/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp b/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp index d183a1f77f..e40cd9c0c7 100644 --- a/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +++ b/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp @@ -1,4 +1,5 @@ #include "seeed_mr60fda2.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" #include @@ -30,7 +31,7 @@ void MR60FDA2Component::dump_config() { // Initialisation functions void MR60FDA2Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up MR60FDA2..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->check_uart_settings(115200); this->current_frame_locate_ = LOCATE_FRAME_HEADER; @@ -335,7 +336,7 @@ void MR60FDA2Component::set_install_height(uint8_t index) { void MR60FDA2Component::set_height_threshold(uint8_t index) { uint8_t send_data[13] = {0x01, 0x00, 0x00, 0x00, 0x04, 0x0E, 0x08, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00}; - float_to_bytes(INSTALL_HEIGHT[index], &send_data[8]); + float_to_bytes(HEIGHT_THRESHOLD[index], &send_data[8]); send_data[12] = calculate_checksum(send_data + 8, 4); this->write_array(send_data, 13); ESP_LOGV(TAG, "SEND HEIGHT THRESHOLD: %s", format_hex_pretty(send_data, 13).c_str()); diff --git a/esphome/components/selec_meter/selec_meter.cpp b/esphome/components/selec_meter/selec_meter.cpp index 8bcf91f3c0..ae41327896 100644 --- a/esphome/components/selec_meter/selec_meter.cpp +++ b/esphome/components/selec_meter/selec_meter.cpp @@ -1,5 +1,6 @@ #include "selec_meter.h" #include "selec_meter_registers.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -83,8 +84,10 @@ void SelecMeter::on_modbus_data(const std::vector &data) { void SelecMeter::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); } void SelecMeter::dump_config() { - ESP_LOGCONFIG(TAG, "SELEC Meter:"); - ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + ESP_LOGCONFIG(TAG, + "SELEC Meter:\n" + " Address: 0x%02X", + this->address_); LOG_SENSOR(" ", "Total Active Energy", this->total_active_energy_sensor_); LOG_SENSOR(" ", "Import Active Energy", this->import_active_energy_sensor_); LOG_SENSOR(" ", "Export Active Energy", this->export_active_energy_sensor_); diff --git a/esphome/components/select/__init__.py b/esphome/components/select/__init__.py index ecbba8677b..e14a9351a0 100644 --- a/esphome/components/select/__init__.py +++ b/esphome/components/select/__init__.py @@ -111,6 +111,7 @@ async def register_select(var, config, *, options: list[str]): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_select(var)) + CORE.register_platform_component("select", var) await setup_select_core_(var, config, options=options) diff --git a/esphome/components/select/select.cpp b/esphome/components/select/select.cpp index 806882ad94..37887da27c 100644 --- a/esphome/components/select/select.cpp +++ b/esphome/components/select/select.cpp @@ -10,7 +10,7 @@ void Select::publish_state(const std::string &state) { auto index = this->index_of(state); const auto *name = this->get_name().c_str(); if (index.has_value()) { - this->has_state_ = true; + this->set_has_state(true); this->state = state; ESP_LOGD(TAG, "'%s': Sending state %s (index %zu)", name, state.c_str(), index.value()); this->state_callback_.call(state, index.value()); diff --git a/esphome/components/select/select.h b/esphome/components/select/select.h index 8ca9a69d1c..3ab651b241 100644 --- a/esphome/components/select/select.h +++ b/esphome/components/select/select.h @@ -35,9 +35,6 @@ class Select : public EntityBase { void publish_state(const std::string &state); - /// Return whether this select component has gotten a full state yet. - bool has_state() const { return has_state_; } - /// Instantiate a SelectCall object to modify this select component's state. SelectCall make_call() { return SelectCall(this); } @@ -73,7 +70,6 @@ class Select : public EntityBase { virtual void control(const std::string &value) = 0; CallbackManager state_callback_; - bool has_state_{false}; }; } // namespace select diff --git a/esphome/components/sen0321/sen0321.cpp b/esphome/components/sen0321/sen0321.cpp index 7801c8c389..c727dda0b1 100644 --- a/esphome/components/sen0321/sen0321.cpp +++ b/esphome/components/sen0321/sen0321.cpp @@ -8,7 +8,7 @@ namespace sen0321_sensor { static const char *const TAG = "sen0321_sensor.sensor"; void Sen0321Sensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up sen0321..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->write_byte(SENSOR_MODE_REGISTER, SENSOR_MODE_AUTO)) { ESP_LOGW(TAG, "Error setting measurement mode."); this->mark_failed(); @@ -21,7 +21,7 @@ void Sen0321Sensor::dump_config() { ESP_LOGCONFIG(TAG, "DF Robot Ozone Sensor sen0321:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with sen0321 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/sen21231/sen21231.cpp b/esphome/components/sen21231/sen21231.cpp index aa123dff62..67001c3f14 100644 --- a/esphome/components/sen21231/sen21231.cpp +++ b/esphome/components/sen21231/sen21231.cpp @@ -12,7 +12,7 @@ void Sen21231Sensor::dump_config() { ESP_LOGCONFIG(TAG, "SEN21231:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with SEN21231 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } ESP_LOGI(TAG, "SEN21231: %s", this->is_failed() ? "FAILED" : "OK"); LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/sen5x/sen5x.cpp b/esphome/components/sen5x/sen5x.cpp index f29ba18d6e..c7fd997b0c 100644 --- a/esphome/components/sen5x/sen5x.cpp +++ b/esphome/components/sen5x/sen5x.cpp @@ -30,7 +30,7 @@ static const int8_t SEN5X_MIN_INDEX_VALUE = 1 * SEN5X_INDEX_SCALE_FACTOR; // static const int16_t SEN5X_MAX_INDEX_VALUE = 500 * SEN5X_INDEX_SCALE_FACTOR; // must be adjusted by the scale factor void SEN5XComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up sen5x..."); + ESP_LOGCONFIG(TAG, "Running setup"); // the sensor needs 1000 ms to enter the idle state this->set_timeout(1000, [this]() { @@ -245,10 +245,10 @@ void SEN5XComponent::dump_config() { if (this->is_failed()) { switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGW(TAG, "Communication failed! Is the sensor connected?"); + ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); break; case MEASUREMENT_INIT_FAILED: - ESP_LOGW(TAG, "Measurement Initialization failed!"); + ESP_LOGW(TAG, "Measurement Initialization failed"); break; case SERIAL_NUMBER_IDENTIFICATION_FAILED: ESP_LOGW(TAG, "Unable to read sensor serial id"); @@ -260,13 +260,16 @@ void SEN5XComponent::dump_config() { ESP_LOGW(TAG, "Unable to read sensor firmware version"); break; default: - ESP_LOGW(TAG, "Unknown setup error!"); + ESP_LOGW(TAG, "Unknown setup error"); break; } } - ESP_LOGCONFIG(TAG, " Productname: %s", this->product_name_.c_str()); - ESP_LOGCONFIG(TAG, " Firmware version: %d", this->firmware_version_); - ESP_LOGCONFIG(TAG, " Serial number %02d.%02d.%02d", serial_number_[0], serial_number_[1], serial_number_[2]); + ESP_LOGCONFIG(TAG, + " Productname: %s\n" + " Firmware version: %d\n" + " Serial number %02d.%02d.%02d", + this->product_name_.c_str(), this->firmware_version_, serial_number_[0], serial_number_[1], + serial_number_[2]); if (this->auto_cleaning_interval_.has_value()) { ESP_LOGCONFIG(TAG, " Auto cleaning interval %" PRId32 " seconds", auto_cleaning_interval_.value()); } diff --git a/esphome/components/sensirion_common/i2c_sensirion.cpp b/esphome/components/sensirion_common/i2c_sensirion.cpp index d8ab817a73..f71b3c14cb 100644 --- a/esphome/components/sensirion_common/i2c_sensirion.cpp +++ b/esphome/components/sensirion_common/i2c_sensirion.cpp @@ -1,6 +1,7 @@ #include "i2c_sensirion.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include namespace esphome { diff --git a/esphome/components/sensirion_common/i2c_sensirion.h b/esphome/components/sensirion_common/i2c_sensirion.h index 24b706cf36..aba93d6cc3 100644 --- a/esphome/components/sensirion_common/i2c_sensirion.h +++ b/esphome/components/sensirion_common/i2c_sensirion.h @@ -1,5 +1,6 @@ #pragma once #include "esphome/components/i2c/i2c.h" +#include "esphome/core/helpers.h" #include diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 051098f6e4..1ad3cfabee 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -43,8 +43,10 @@ from esphome.const import ( CONF_WINDOW_SIZE, DEVICE_CLASS_APPARENT_POWER, DEVICE_CLASS_AQI, + DEVICE_CLASS_AREA, DEVICE_CLASS_ATMOSPHERIC_PRESSURE, DEVICE_CLASS_BATTERY, + DEVICE_CLASS_BLOOD_GLUCOSE_CONCENTRATION, DEVICE_CLASS_CARBON_DIOXIDE, DEVICE_CLASS_CARBON_MONOXIDE, DEVICE_CLASS_CONDUCTIVITY, @@ -56,6 +58,7 @@ from esphome.const import ( DEVICE_CLASS_DURATION, DEVICE_CLASS_EMPTY, DEVICE_CLASS_ENERGY, + DEVICE_CLASS_ENERGY_DISTANCE, DEVICE_CLASS_ENERGY_STORAGE, DEVICE_CLASS_FREQUENCY, DEVICE_CLASS_GAS, @@ -77,6 +80,7 @@ from esphome.const import ( DEVICE_CLASS_PRECIPITATION, DEVICE_CLASS_PRECIPITATION_INTENSITY, DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_REACTIVE_ENERGY, DEVICE_CLASS_REACTIVE_POWER, DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_SOUND_PRESSURE, @@ -92,6 +96,7 @@ from esphome.const import ( DEVICE_CLASS_VOLUME_STORAGE, DEVICE_CLASS_WATER, DEVICE_CLASS_WEIGHT, + DEVICE_CLASS_WIND_DIRECTION, DEVICE_CLASS_WIND_SPEED, ENTITY_CATEGORY_CONFIG, ) @@ -104,8 +109,10 @@ CODEOWNERS = ["@esphome/core"] DEVICE_CLASSES = [ DEVICE_CLASS_APPARENT_POWER, DEVICE_CLASS_AQI, + DEVICE_CLASS_AREA, DEVICE_CLASS_ATMOSPHERIC_PRESSURE, DEVICE_CLASS_BATTERY, + DEVICE_CLASS_BLOOD_GLUCOSE_CONCENTRATION, DEVICE_CLASS_CARBON_DIOXIDE, DEVICE_CLASS_CARBON_MONOXIDE, DEVICE_CLASS_CONDUCTIVITY, @@ -117,6 +124,7 @@ DEVICE_CLASSES = [ DEVICE_CLASS_DURATION, DEVICE_CLASS_EMPTY, DEVICE_CLASS_ENERGY, + DEVICE_CLASS_ENERGY_DISTANCE, DEVICE_CLASS_ENERGY_STORAGE, DEVICE_CLASS_FREQUENCY, DEVICE_CLASS_GAS, @@ -138,6 +146,7 @@ DEVICE_CLASSES = [ DEVICE_CLASS_PRECIPITATION, DEVICE_CLASS_PRECIPITATION_INTENSITY, DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_REACTIVE_ENERGY, DEVICE_CLASS_REACTIVE_POWER, DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_SOUND_PRESSURE, @@ -153,11 +162,11 @@ DEVICE_CLASSES = [ DEVICE_CLASS_VOLUME_STORAGE, DEVICE_CLASS_WATER, DEVICE_CLASS_WEIGHT, + DEVICE_CLASS_WIND_DIRECTION, DEVICE_CLASS_WIND_SPEED, ] _LOGGER = logging.getLogger(__name__) - sensor_ns = cg.esphome_ns.namespace("sensor") StateClasses = sensor_ns.enum("StateClass") STATE_CLASSES = { @@ -830,6 +839,7 @@ async def register_sensor(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_sensor(var)) + CORE.register_platform_component("sensor", var) await setup_sensor_core_(var, config) diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index 3cfaebb708..94fec8208b 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -3,9 +3,9 @@ #include #include #include +#include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" -#include "esphome/core/automation.h" namespace esphome { namespace sensor { diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index 14a8b3d490..6d6cff0400 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -38,7 +38,9 @@ StateClass Sensor::get_state_class() { void Sensor::publish_state(float state) { this->raw_state = state; - this->raw_callback_.call(state); + if (this->raw_callback_) { + this->raw_callback_->call(state); + } ESP_LOGV(TAG, "'%s': Received new state %f", this->name_.c_str(), state); @@ -51,7 +53,10 @@ void Sensor::publish_state(float state) { void Sensor::add_on_state_callback(std::function &&callback) { this->callback_.add(std::move(callback)); } void Sensor::add_on_raw_state_callback(std::function &&callback) { - this->raw_callback_.add(std::move(callback)); + if (!this->raw_callback_) { + this->raw_callback_ = make_unique>(); + } + this->raw_callback_->add(std::move(callback)); } void Sensor::add_filter(Filter *filter) { @@ -88,13 +93,12 @@ float Sensor::get_raw_state() const { return this->raw_state; } std::string Sensor::unique_id() { return ""; } void Sensor::internal_send_state_to_frontend(float state) { - this->has_state_ = true; + this->set_has_state(true); this->state = state; ESP_LOGD(TAG, "'%s': Sending state %.5f %s with %d decimals of accuracy", this->get_name().c_str(), state, this->get_unit_of_measurement().c_str(), this->get_accuracy_decimals()); this->callback_.call(state); } -bool Sensor::has_state() const { return this->has_state_; } } // namespace sensor } // namespace esphome diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index 98356c943d..456e876497 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -1,25 +1,30 @@ #pragma once -#include "esphome/core/log.h" #include "esphome/core/component.h" #include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/components/sensor/filter.h" #include +#include namespace esphome { namespace sensor { #define LOG_SENSOR(prefix, type, obj) \ if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ + ESP_LOGCONFIG(TAG, \ + "%s%s '%s'\n" \ + "%s State Class: '%s'\n" \ + "%s Unit of Measurement: '%s'\n" \ + "%s Accuracy Decimals: %d", \ + prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str(), prefix, \ + state_class_to_string((obj)->get_state_class()).c_str(), prefix, \ + (obj)->get_unit_of_measurement().c_str(), prefix, (obj)->get_accuracy_decimals()); \ if (!(obj)->get_device_class().empty()) { \ ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \ } \ - ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, state_class_to_string((obj)->get_state_class()).c_str()); \ - ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, (obj)->get_unit_of_measurement().c_str()); \ - ESP_LOGCONFIG(TAG, "%s Accuracy Decimals: %d", prefix, (obj)->get_accuracy_decimals()); \ if (!(obj)->get_icon().empty()) { \ ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \ } \ @@ -136,9 +141,6 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa */ float raw_state; - /// Return whether this sensor has gotten a full state (that passed through all filters) yet. - bool has_state() const; - /** Override this method to set the unique ID of this sensor. * * @deprecated Do not use for new sensors, a suitable unique ID is automatically generated (2023.4). @@ -148,15 +150,14 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa void internal_send_state_to_frontend(float state); protected: - CallbackManager raw_callback_; ///< Storage for raw state callbacks. - CallbackManager callback_; ///< Storage for filtered state callbacks. + std::unique_ptr> raw_callback_; ///< Storage for raw state callbacks (lazy allocated). + CallbackManager callback_; ///< Storage for filtered state callbacks. Filter *filter_list_{nullptr}; ///< Store all active filters. optional accuracy_decimals_; ///< Accuracy in decimals override optional state_class_{STATE_CLASS_NONE}; ///< State class override bool force_update_{false}; ///< Force update mode - bool has_state_{false}; }; } // namespace sensor diff --git a/esphome/components/servo/servo.cpp b/esphome/components/servo/servo.cpp index 18e8c8087e..b8546d345c 100644 --- a/esphome/components/servo/servo.cpp +++ b/esphome/components/servo/servo.cpp @@ -11,12 +11,15 @@ static const char *const TAG = "servo"; uint32_t global_servo_id = 1911044085ULL; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void Servo::dump_config() { - ESP_LOGCONFIG(TAG, "Servo:"); - ESP_LOGCONFIG(TAG, " Idle Level: %.1f%%", this->idle_level_ * 100.0f); - ESP_LOGCONFIG(TAG, " Min Level: %.1f%%", this->min_level_ * 100.0f); - ESP_LOGCONFIG(TAG, " Max Level: %.1f%%", this->max_level_ * 100.0f); - ESP_LOGCONFIG(TAG, " auto detach time: %" PRIu32 " ms", this->auto_detach_time_); - ESP_LOGCONFIG(TAG, " run duration: %" PRIu32 " ms", this->transition_length_); + ESP_LOGCONFIG(TAG, + "Servo:\n" + " Idle Level: %.1f%%\n" + " Min Level: %.1f%%\n" + " Max Level: %.1f%%\n" + " Auto-detach time: %" PRIu32 " ms\n" + " Run duration: %" PRIu32 " ms", + this->idle_level_ * 100.0f, this->min_level_ * 100.0f, this->max_level_ * 100.0f, + this->auto_detach_time_, this->transition_length_); } void Servo::setup() { @@ -41,7 +44,7 @@ void Servo::loop() { if (millis() - this->start_millis_ > this->auto_detach_time_) { this->detach(); this->start_millis_ = 0; - ESP_LOGD(TAG, "Servo detached on auto_detach_time"); + ESP_LOGD(TAG, "Detached on auto_detach_time"); } } if (this->target_value_ != this->current_value_ && this->state_ == STATE_ATTACHED) { @@ -63,7 +66,7 @@ void Servo::loop() { if (this->target_value_ == this->current_value_ && this->state_ == STATE_ATTACHED) { this->state_ = STATE_TARGET_REACHED; this->start_millis_ = millis(); // set current stamp for potential auto_detach_time_ check - ESP_LOGD(TAG, "Servo reached target"); + ESP_LOGD(TAG, "Reached target"); } } @@ -78,7 +81,7 @@ void Servo::write(float value) { this->source_value_ = this->current_value_; this->state_ = STATE_ATTACHED; this->start_millis_ = millis(); - ESP_LOGD(TAG, "Servo new target: %f", value); + ESP_LOGD(TAG, "New target: %f", value); } void Servo::internal_write(float value) { diff --git a/esphome/components/servo/servo.h b/esphome/components/servo/servo.h index 13a7472ae5..92d18bf601 100644 --- a/esphome/components/servo/servo.h +++ b/esphome/components/servo/servo.h @@ -1,7 +1,7 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/core/automation.h" +#include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" #include "esphome/components/output/float_output.h" diff --git a/esphome/components/sfa30/sfa30.cpp b/esphome/components/sfa30/sfa30.cpp index 20d5ddad5e..c521b3aa02 100644 --- a/esphome/components/sfa30/sfa30.cpp +++ b/esphome/components/sfa30/sfa30.cpp @@ -11,7 +11,7 @@ static const uint16_t SFA30_CMD_START_CONTINUOUS_MEASUREMENTS = 0x0006; static const uint16_t SFA30_CMD_READ_MEASUREMENT = 0x0327; void SFA30Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up sfa30..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Serial Number identification uint16_t raw_device_marking[16]; diff --git a/esphome/components/sgp30/sgp30.cpp b/esphome/components/sgp30/sgp30.cpp index 77e9ef9820..0c7f25b699 100644 --- a/esphome/components/sgp30/sgp30.cpp +++ b/esphome/components/sgp30/sgp30.cpp @@ -33,7 +33,7 @@ const uint32_t SHORTEST_BASELINE_STORE_INTERVAL = 3600; const uint32_t MAXIMUM_STORAGE_DIFF = 50; void SGP30Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up SGP30..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Serial Number identification uint16_t raw_serial_number[3]; @@ -232,27 +232,29 @@ void SGP30Component::dump_config() { if (this->is_failed()) { switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGW(TAG, "Communication failed! Is the sensor connected?"); + ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); break; case MEASUREMENT_INIT_FAILED: - ESP_LOGW(TAG, "Measurement Initialization failed!"); + ESP_LOGW(TAG, "Measurement Initialization failed"); break; case INVALID_ID: ESP_LOGW(TAG, "Sensor reported an invalid ID. Is this an SGP30?"); break; case UNSUPPORTED_ID: - ESP_LOGW(TAG, "Sensor reported an unsupported ID (SGPC3)."); + ESP_LOGW(TAG, "Sensor reported an unsupported ID (SGPC3)"); break; default: - ESP_LOGW(TAG, "Unknown setup error!"); + ESP_LOGW(TAG, "Unknown setup error"); break; } } else { ESP_LOGCONFIG(TAG, " Serial number: %" PRIu64, this->serial_number_); if (this->eco2_baseline_ != 0x0000 && this->tvoc_baseline_ != 0x0000) { - ESP_LOGCONFIG(TAG, " Baseline:"); - ESP_LOGCONFIG(TAG, " eCO2 Baseline: 0x%04X", this->eco2_baseline_); - ESP_LOGCONFIG(TAG, " TVOC Baseline: 0x%04X", this->tvoc_baseline_); + ESP_LOGCONFIG(TAG, + " Baseline:\n" + " eCO2 Baseline: 0x%04X\n" + " TVOC Baseline: 0x%04X", + this->eco2_baseline_, this->tvoc_baseline_); } else { ESP_LOGCONFIG(TAG, " Baseline: No baseline configured"); } diff --git a/esphome/components/sgp4x/sgp4x.cpp b/esphome/components/sgp4x/sgp4x.cpp index bf91c90832..bd84ae97f3 100644 --- a/esphome/components/sgp4x/sgp4x.cpp +++ b/esphome/components/sgp4x/sgp4x.cpp @@ -9,34 +9,34 @@ namespace sgp4x { static const char *const TAG = "sgp4x"; void SGP4xComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up SGP4x..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Serial Number identification uint16_t raw_serial_number[3]; if (!this->get_register(SGP4X_CMD_GET_SERIAL_ID, raw_serial_number, 3, 1)) { - ESP_LOGE(TAG, "Failed to read serial number"); + ESP_LOGE(TAG, "Get serial number failed"); this->error_code_ = SERIAL_NUMBER_IDENTIFICATION_FAILED; this->mark_failed(); return; } this->serial_number_ = (uint64_t(raw_serial_number[0]) << 24) | (uint64_t(raw_serial_number[1]) << 16) | (uint64_t(raw_serial_number[2])); - ESP_LOGD(TAG, "Serial Number: %" PRIu64, this->serial_number_); + ESP_LOGD(TAG, "Serial number: %" PRIu64, this->serial_number_); // Featureset identification for future use - uint16_t raw_featureset; - if (!this->get_register(SGP4X_CMD_GET_FEATURESET, raw_featureset, 1)) { - ESP_LOGD(TAG, "raw_featureset write_command_ failed"); + uint16_t featureset; + if (!this->get_register(SGP4X_CMD_GET_FEATURESET, featureset, 1)) { + ESP_LOGD(TAG, "Get feature set failed"); this->mark_failed(); return; } - this->featureset_ = raw_featureset; - if ((this->featureset_ & 0x1FF) == SGP40_FEATURESET) { - sgp_type_ = SGP40; - self_test_time_ = SPG40_SELFTEST_TIME; - measure_time_ = SGP40_MEASURE_TIME; + featureset &= 0x1FF; + if (featureset == SGP40_FEATURESET) { + this->sgp_type_ = SGP40; + this->self_test_time_ = SPG40_SELFTEST_TIME; + this->measure_time_ = SGP40_MEASURE_TIME; if (this->nox_sensor_) { - ESP_LOGE(TAG, "Measuring NOx requires a SGP41 sensor but a SGP40 sensor is detected"); + ESP_LOGE(TAG, "SGP41 required for NOx"); // disable the sensor this->nox_sensor_->set_disabled_by_default(true); // make sure it's not visible in HA @@ -45,20 +45,17 @@ void SGP4xComponent::setup() { // remove pointer to sensor this->nox_sensor_ = nullptr; } + } else if (featureset == SGP41_FEATURESET) { + this->sgp_type_ = SGP41; + this->self_test_time_ = SPG41_SELFTEST_TIME; + this->measure_time_ = SGP41_MEASURE_TIME; } else { - if ((this->featureset_ & 0x1FF) == SGP41_FEATURESET) { - sgp_type_ = SGP41; - self_test_time_ = SPG41_SELFTEST_TIME; - measure_time_ = SGP41_MEASURE_TIME; - } else { - ESP_LOGD(TAG, "Product feature set failed 0x%0X , expecting 0x%0X", uint16_t(this->featureset_ & 0x1FF), - SGP40_FEATURESET); - this->mark_failed(); - return; - } + ESP_LOGD(TAG, "Unknown feature set 0x%0X", featureset); + this->mark_failed(); + return; } - ESP_LOGD(TAG, "Product version: 0x%0X", uint16_t(this->featureset_ & 0x1FF)); + ESP_LOGD(TAG, "Version 0x%0X", featureset); if (this->store_baseline_) { // Hash with compilation time and serial number @@ -70,7 +67,7 @@ void SGP4xComponent::setup() { if (this->pref_.load(&this->voc_baselines_storage_)) { this->voc_state0_ = this->voc_baselines_storage_.state0; this->voc_state1_ = this->voc_baselines_storage_.state1; - ESP_LOGI(TAG, "Loaded VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32, + ESP_LOGV(TAG, "Loaded VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32, this->voc_baselines_storage_.state0, voc_baselines_storage_.state1); } @@ -78,7 +75,7 @@ void SGP4xComponent::setup() { this->seconds_since_last_store_ = 0; if (this->voc_baselines_storage_.state0 > 0 && this->voc_baselines_storage_.state1 > 0) { - ESP_LOGI(TAG, "Setting VOC baseline from save state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32, + ESP_LOGV(TAG, "Setting VOC baseline from save state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32, this->voc_baselines_storage_.state0, voc_baselines_storage_.state1); voc_algorithm_.set_states(this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1); } @@ -110,39 +107,29 @@ void SGP4xComponent::setup() { limit the amount of communication done over wifi for power consumption or to keep the number of records reported from being overwhelming. */ - ESP_LOGD(TAG, "Component requires sampling of 1Hz, setting up background sampler"); + ESP_LOGV(TAG, "Component requires sampling of 1Hz, setting up background sampler"); this->set_interval(1000, [this]() { this->take_sample(); }); } void SGP4xComponent::self_test_() { - ESP_LOGD(TAG, "Self-test started"); + ESP_LOGD(TAG, "Starting self-test"); if (!this->write_command(SGP4X_CMD_SELF_TEST)) { this->error_code_ = COMMUNICATION_FAILED; - ESP_LOGD(TAG, "Self-test communication failed"); + ESP_LOGD(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); } - this->set_timeout(self_test_time_, [this]() { - uint16_t reply; - if (!this->read_data(reply)) { + this->set_timeout(this->self_test_time_, [this]() { + uint16_t reply = 0; + if (!this->read_data(reply) || (reply != 0xD400)) { this->error_code_ = SELF_TEST_FAILED; - ESP_LOGD(TAG, "Self-test read_data_ failed"); + ESP_LOGW(TAG, "Self-test failed (0x%X)", reply); this->mark_failed(); return; } - if (reply == 0xD400) { - this->self_test_complete_ = true; - ESP_LOGD(TAG, "Self-test completed"); - return; - } else { - this->error_code_ = SELF_TEST_FAILED; - ESP_LOGD(TAG, "Self-test failed 0x%X", reply); - return; - } - - ESP_LOGD(TAG, "Self-test failed 0x%X", reply); - this->mark_failed(); + this->self_test_complete_ = true; + ESP_LOGD(TAG, "Self-test complete"); }); } @@ -150,7 +137,7 @@ void SGP4xComponent::update_gas_indices_() { this->voc_index_ = this->voc_algorithm_.process(this->voc_sraw_); if (this->nox_sensor_ != nullptr) this->nox_index_ = this->nox_algorithm_.process(this->nox_sraw_); - ESP_LOGV(TAG, "VOC = %" PRId32 ", NOx = %" PRId32, this->voc_index_, this->nox_index_); + ESP_LOGV(TAG, "VOC: %" PRId32 ", NOx: %" PRId32, this->voc_index_, this->nox_index_); // Store baselines after defined interval or if the difference between current and stored baseline becomes too // much if (this->store_baseline_ && this->seconds_since_last_store_ > SHORTEST_BASELINE_STORE_INTERVAL) { @@ -162,18 +149,18 @@ void SGP4xComponent::update_gas_indices_() { this->voc_baselines_storage_.state1 = this->voc_state1_; if (this->pref_.save(&this->voc_baselines_storage_)) { - ESP_LOGI(TAG, "Stored VOC baseline state0: 0x%04" PRIX32 " ,state1: 0x%04" PRIX32, + ESP_LOGV(TAG, "Stored VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32, this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1); } else { - ESP_LOGW(TAG, "Could not store VOC baselines"); + ESP_LOGW(TAG, "Storing VOC baselines failed"); } } } if (this->samples_read_ < this->samples_to_stabilize_) { this->samples_read_++; - ESP_LOGD(TAG, "Sensor has not collected enough samples yet. (%d/%d) VOC index is: %" PRIu32, this->samples_read_, - this->samples_to_stabilize_, this->voc_index_); + ESP_LOGD(TAG, "Stabilizing (%d/%d); VOC index: %" PRIu32, this->samples_read_, this->samples_to_stabilize_, + this->voc_index_); } } @@ -182,7 +169,7 @@ void SGP4xComponent::measure_raw_() { static uint32_t nox_conditioning_start = millis(); if (!this->self_test_complete_) { - ESP_LOGD(TAG, "Self-test not yet complete"); + ESP_LOGW(TAG, "Self-test incomplete"); return; } if (this->humidity_sensor_ != nullptr) { @@ -270,37 +257,38 @@ void SGP4xComponent::update() { void SGP4xComponent::dump_config() { ESP_LOGCONFIG(TAG, "SGP4x:"); LOG_I2C_DEVICE(this); - ESP_LOGCONFIG(TAG, " store_baseline: %d", this->store_baseline_); + ESP_LOGCONFIG(TAG, " Store baseline: %s", YESNO(this->store_baseline_)); if (this->is_failed()) { switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGW(TAG, "Communication failed! Is the sensor connected?"); + ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); break; case SERIAL_NUMBER_IDENTIFICATION_FAILED: - ESP_LOGW(TAG, "Get Serial number failed."); + ESP_LOGW(TAG, "Get serial number failed"); break; case SELF_TEST_FAILED: - ESP_LOGW(TAG, "Self test failed."); + ESP_LOGW(TAG, "Self-test failed"); break; - default: - ESP_LOGW(TAG, "Unknown setup error!"); + ESP_LOGW(TAG, "Unknown error"); break; } } else { - ESP_LOGCONFIG(TAG, " Type: %s", sgp_type_ == SGP41 ? "SGP41" : "SPG40"); - ESP_LOGCONFIG(TAG, " Serial number: %" PRIu64, this->serial_number_); - ESP_LOGCONFIG(TAG, " Minimum Samples: %f", GasIndexAlgorithm_INITIAL_BLACKOUT); + ESP_LOGCONFIG(TAG, + " Type: %s\n" + " Serial number: %" PRIu64 "\n" + " Minimum Samples: %f", + sgp_type_ == SGP41 ? "SGP41" : "SPG40", this->serial_number_, GasIndexAlgorithm_INITIAL_BLACKOUT); } LOG_UPDATE_INTERVAL(this); + ESP_LOGCONFIG(TAG, " Compensation:"); if (this->humidity_sensor_ != nullptr || this->temperature_sensor_ != nullptr) { - ESP_LOGCONFIG(TAG, " Compensation:"); LOG_SENSOR(" ", "Temperature Source:", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity Source:", this->humidity_sensor_); } else { - ESP_LOGCONFIG(TAG, " Compensation: No source configured"); + ESP_LOGCONFIG(TAG, " No source configured"); } LOG_SENSOR(" ", "VOC", this->voc_sensor_); LOG_SENSOR(" ", "NOx", this->nox_sensor_); diff --git a/esphome/components/sgp4x/sgp4x.h b/esphome/components/sgp4x/sgp4x.h index 959ff12c27..45ee66af68 100644 --- a/esphome/components/sgp4x/sgp4x.h +++ b/esphome/components/sgp4x/sgp4x.h @@ -115,7 +115,6 @@ class SGP4xComponent : public PollingComponent, public sensor::Sensor, public se SgpType sgp_type_{SGP40}; uint64_t serial_number_; - uint16_t featureset_; bool self_test_complete_; uint16_t self_test_time_; diff --git a/esphome/components/shelly_dimmer/shelly_dimmer.cpp b/esphome/components/shelly_dimmer/shelly_dimmer.cpp index d4229b2384..6b4ab13c48 100644 --- a/esphome/components/shelly_dimmer/shelly_dimmer.cpp +++ b/esphome/components/shelly_dimmer/shelly_dimmer.cpp @@ -101,7 +101,7 @@ void ShellyDimmer::setup() { this->pin_nrst_->setup(); this->pin_boot0_->setup(); - ESP_LOGI(TAG, "Initializing Shelly Dimmer..."); + ESP_LOGI(TAG, "Initializing"); this->handle_firmware(); @@ -119,17 +119,21 @@ void ShellyDimmer::dump_config() { LOG_PIN(" NRST Pin: ", this->pin_nrst_); LOG_PIN(" BOOT0 Pin: ", this->pin_boot0_); - ESP_LOGCONFIG(TAG, " Leading Edge: %s", YESNO(this->leading_edge_)); - ESP_LOGCONFIG(TAG, " Warmup Brightness: %d", this->warmup_brightness_); + ESP_LOGCONFIG(TAG, + " Leading Edge: %s\n" + " Warmup Brightness: %d\n" + " Minimum Brightness: %d\n" + " Maximum Brightness: %d", + YESNO(this->leading_edge_), this->warmup_brightness_, this->min_brightness_, this->max_brightness_); // ESP_LOGCONFIG(TAG, " Warmup Time: %d", this->warmup_time_); // ESP_LOGCONFIG(TAG, " Fade Rate: %d", this->fade_rate_); - ESP_LOGCONFIG(TAG, " Minimum Brightness: %d", this->min_brightness_); - ESP_LOGCONFIG(TAG, " Maximum Brightness: %d", this->max_brightness_); LOG_UPDATE_INTERVAL(this); - ESP_LOGCONFIG(TAG, " STM32 current firmware version: %d.%d ", this->version_major_, this->version_minor_); - ESP_LOGCONFIG(TAG, " STM32 required firmware version: %d.%d", USE_SHD_FIRMWARE_MAJOR_VERSION, + ESP_LOGCONFIG(TAG, + " STM32 current firmware version: %d.%d \n" + " STM32 required firmware version: %d.%d", + this->version_major_, this->version_minor_, USE_SHD_FIRMWARE_MAJOR_VERSION, USE_SHD_FIRMWARE_MINOR_VERSION); if (this->version_major_ != USE_SHD_FIRMWARE_MAJOR_VERSION || diff --git a/esphome/components/sht3xd/sht3xd.cpp b/esphome/components/sht3xd/sht3xd.cpp index ffaf5db322..9dc866ddc3 100644 --- a/esphome/components/sht3xd/sht3xd.cpp +++ b/esphome/components/sht3xd/sht3xd.cpp @@ -25,7 +25,7 @@ static const uint16_t SHT3XD_COMMAND_POLLING_H = 0x2400; static const uint16_t SHT3XD_COMMAND_FETCH_DATA = 0xE000; void SHT3XDComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up SHT3xD..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint16_t raw_serial_number[2]; if (!this->get_register(SHT3XD_COMMAND_READ_SERIAL_NUMBER_CLOCK_STRETCHING, raw_serial_number, 2)) { this->error_code_ = READ_SERIAL_STRETCHED_FAILED; diff --git a/esphome/components/sht4x/sht4x.cpp b/esphome/components/sht4x/sht4x.cpp index e4fa16d87a..944b13023e 100644 --- a/esphome/components/sht4x/sht4x.cpp +++ b/esphome/components/sht4x/sht4x.cpp @@ -18,7 +18,7 @@ void SHT4XComponent::start_heater_() { } void SHT4XComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up sht4x..."); + ESP_LOGCONFIG(TAG, "Running setup"); auto err = this->write(nullptr, 0); if (err != i2c::ERROR_OK) { @@ -59,7 +59,7 @@ void SHT4XComponent::dump_config() { ESP_LOGCONFIG(TAG, "SHT4x:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with SHT4x failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } diff --git a/esphome/components/shtcx/shtcx.cpp b/esphome/components/shtcx/shtcx.cpp index 0de56a8044..5420119bd6 100644 --- a/esphome/components/shtcx/shtcx.cpp +++ b/esphome/components/shtcx/shtcx.cpp @@ -20,24 +20,18 @@ inline const char *to_string(SHTCXType type) { case SHTCX_TYPE_SHTC1: return "SHTC1"; default: - return "[Unknown model]"; + return "UNKNOWN"; } } void SHTCXComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up SHTCx..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->wake_up(); this->soft_reset(); - if (!this->write_command(SHTCX_COMMAND_READ_ID_REGISTER)) { - ESP_LOGE(TAG, "Error requesting Device ID"); - this->mark_failed(); - return; - } - uint16_t device_id_register; - if (!this->read_data(&device_id_register, 1)) { - ESP_LOGE(TAG, "Error reading Device ID"); + if (!this->write_command(SHTCX_COMMAND_READ_ID_REGISTER) || !this->read_data(&device_id_register, 1)) { + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); return; } @@ -53,31 +47,33 @@ void SHTCXComponent::setup() { } else { this->type_ = SHTCX_TYPE_UNKNOWN; } - ESP_LOGCONFIG(TAG, " Device identified: %s (%04x)", to_string(this->type_), device_id_register); } + void SHTCXComponent::dump_config() { ESP_LOGCONFIG(TAG, "SHTCx:"); ESP_LOGCONFIG(TAG, " Model: %s (%04x)", to_string(this->type_), this->sensor_id_); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with SHTCx failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); } + float SHTCXComponent::get_setup_priority() const { return setup_priority::DATA; } + void SHTCXComponent::update() { if (this->status_has_warning()) { - ESP_LOGW(TAG, "Retrying to reconnect the sensor."); + ESP_LOGW(TAG, "Retrying communication"); this->soft_reset(); } if (this->type_ != SHTCX_TYPE_SHTC1) { this->wake_up(); } if (!this->write_command(SHTCX_COMMAND_POLLING_H)) { - ESP_LOGE(TAG, "sensor polling failed"); + ESP_LOGE(TAG, "Polling failed"); if (this->temperature_sensor_ != nullptr) this->temperature_sensor_->publish_state(NAN); if (this->humidity_sensor_ != nullptr) @@ -91,13 +87,13 @@ void SHTCXComponent::update() { float humidity = NAN; uint16_t raw_data[2]; if (!this->read_data(raw_data, 2)) { - ESP_LOGE(TAG, "sensor read failed"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning(); } else { temperature = 175.0f * float(raw_data[0]) / 65536.0f - 45.0f; humidity = 100.0f * float(raw_data[1]) / 65536.0f; - ESP_LOGD(TAG, "Got temperature=%.2f°C humidity=%.2f%%", temperature, humidity); + ESP_LOGD(TAG, "Temperature=%.2f°C Humidity=%.2f%%", temperature, humidity); } if (this->temperature_sensor_ != nullptr) this->temperature_sensor_->publish_state(temperature); @@ -114,6 +110,7 @@ void SHTCXComponent::soft_reset() { this->write_command(SHTCX_COMMAND_SOFT_RESET); delayMicroseconds(200); } + void SHTCXComponent::sleep() { this->write_command(SHTCX_COMMAND_SLEEP); } void SHTCXComponent::wake_up() { diff --git a/esphome/components/shutdown/button/shutdown_button.cpp b/esphome/components/shutdown/button/shutdown_button.cpp index be88a10d49..b40af7517b 100644 --- a/esphome/components/shutdown/button/shutdown_button.cpp +++ b/esphome/components/shutdown/button/shutdown_button.cpp @@ -17,7 +17,7 @@ static const char *const TAG = "shutdown.button"; void ShutdownButton::dump_config() { LOG_BUTTON("", "Shutdown Button", this); } void ShutdownButton::press_action() { - ESP_LOGI(TAG, "Shutting down..."); + ESP_LOGI(TAG, "Shutting down"); // Let MQTT settle a bit delay(100); // NOLINT App.run_safe_shutdown_hooks(); diff --git a/esphome/components/shutdown/switch/shutdown_switch.cpp b/esphome/components/shutdown/switch/shutdown_switch.cpp index a5f9a92982..b685ab14ab 100644 --- a/esphome/components/shutdown/switch/shutdown_switch.cpp +++ b/esphome/components/shutdown/switch/shutdown_switch.cpp @@ -21,7 +21,7 @@ void ShutdownSwitch::write_state(bool state) { this->publish_state(false); if (state) { - ESP_LOGI(TAG, "Shutting down..."); + ESP_LOGI(TAG, "Shutting down"); delay(100); // NOLINT App.run_safe_shutdown_hooks(); diff --git a/esphome/components/sim800l/sim800l.cpp b/esphome/components/sim800l/sim800l.cpp index 38775b0062..d97b0ae364 100644 --- a/esphome/components/sim800l/sim800l.cpp +++ b/esphome/components/sim800l/sim800l.cpp @@ -28,7 +28,7 @@ void Sim800LComponent::update() { this->state_ = STATE_DIALING1; } else if (this->registered_ && this->connect_pending_) { this->connect_pending_ = false; - ESP_LOGI(TAG, "Connecting..."); + ESP_LOGI(TAG, "Connecting"); this->send_cmd_("ATA"); this->state_ = STATE_ATA_SENT; } else if (this->registered_ && this->send_ussd_pending_) { @@ -36,7 +36,7 @@ void Sim800LComponent::update() { this->state_ = STATE_SEND_USSD1; } else if (this->registered_ && this->disconnect_pending_) { this->disconnect_pending_ = false; - ESP_LOGI(TAG, "Disconnecting..."); + ESP_LOGI(TAG, "Disconnecting"); this->send_cmd_("ATH"); } else if (this->registered_ && this->call_state_ != 6) { send_cmd_("AT+CLCC"); diff --git a/esphome/components/slow_pwm/slow_pwm_output.cpp b/esphome/components/slow_pwm/slow_pwm_output.cpp index 643294303c..48ded94b3a 100644 --- a/esphome/components/slow_pwm/slow_pwm_output.cpp +++ b/esphome/components/slow_pwm/slow_pwm_output.cpp @@ -63,8 +63,10 @@ void SlowPWMOutput::dump_config() { if (this->turn_off_trigger_) { ESP_LOGCONFIG(TAG, " Turn off automation configured"); } - ESP_LOGCONFIG(TAG, " Period: %d ms", this->period_); - ESP_LOGCONFIG(TAG, " Restart cycle on state change: %s", YESNO(this->restart_cycle_on_state_change_)); + ESP_LOGCONFIG(TAG, + " Period: %d ms\n" + " Restart cycle on state change: %s", + this->period_, YESNO(this->restart_cycle_on_state_change_)); LOG_FLOAT_OUTPUT(this); } diff --git a/esphome/components/sm16716/sm16716.cpp b/esphome/components/sm16716/sm16716.cpp index 373fbd4766..b25f935eba 100644 --- a/esphome/components/sm16716/sm16716.cpp +++ b/esphome/components/sm16716/sm16716.cpp @@ -7,7 +7,7 @@ namespace sm16716 { static const char *const TAG = "sm16716"; void SM16716::setup() { - ESP_LOGCONFIG(TAG, "Setting up SM16716OutputComponent..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->data_pin_->setup(); this->data_pin_->digital_write(false); this->clock_pin_->setup(); diff --git a/esphome/components/sm2135/sm2135.cpp b/esphome/components/sm2135/sm2135.cpp index ee5948bb3a..cd647ef3b9 100644 --- a/esphome/components/sm2135/sm2135.cpp +++ b/esphome/components/sm2135/sm2135.cpp @@ -20,7 +20,7 @@ static const uint8_t SM2135_RGB = 0x00; // RGB channel static const uint8_t SM2135_CW = 0x80; // CW channel (Chip default) void SM2135::setup() { - ESP_LOGCONFIG(TAG, "Setting up SM2135OutputComponent..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->data_pin_->setup(); this->data_pin_->digital_write(false); this->data_pin_->pin_mode(gpio::FLAG_OUTPUT); diff --git a/esphome/components/sm2235/sm2235.cpp b/esphome/components/sm2235/sm2235.cpp index f953d41957..e9f84773e2 100644 --- a/esphome/components/sm2235/sm2235.cpp +++ b/esphome/components/sm2235/sm2235.cpp @@ -7,7 +7,7 @@ namespace sm2235 { static const char *const TAG = "sm2235"; void SM2235::setup() { - ESP_LOGCONFIG(TAG, "Setting up sm2235 Output Component..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->data_pin_->setup(); this->data_pin_->digital_write(true); this->clock_pin_->setup(); @@ -19,8 +19,10 @@ void SM2235::dump_config() { ESP_LOGCONFIG(TAG, "sm2235:"); LOG_PIN(" Data Pin: ", this->data_pin_); LOG_PIN(" Clock Pin: ", this->clock_pin_); - ESP_LOGCONFIG(TAG, " Color Channels Max Power: %u", this->max_power_color_channels_); - ESP_LOGCONFIG(TAG, " White Channels Max Power: %u", this->max_power_white_channels_); + ESP_LOGCONFIG(TAG, + " Color Channels Max Power: %u\n" + " White Channels Max Power: %u", + this->max_power_color_channels_, this->max_power_white_channels_); } } // namespace sm2235 diff --git a/esphome/components/sm2335/sm2335.cpp b/esphome/components/sm2335/sm2335.cpp index b6c482b5bb..99b722a639 100644 --- a/esphome/components/sm2335/sm2335.cpp +++ b/esphome/components/sm2335/sm2335.cpp @@ -7,7 +7,7 @@ namespace sm2335 { static const char *const TAG = "sm2335"; void SM2335::setup() { - ESP_LOGCONFIG(TAG, "Setting up sm2335 Output Component..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->data_pin_->setup(); this->data_pin_->digital_write(true); this->clock_pin_->setup(); @@ -19,8 +19,10 @@ void SM2335::dump_config() { ESP_LOGCONFIG(TAG, "sm2335:"); LOG_PIN(" Data Pin: ", this->data_pin_); LOG_PIN(" Clock Pin: ", this->clock_pin_); - ESP_LOGCONFIG(TAG, " Color Channels Max Power: %u", this->max_power_color_channels_); - ESP_LOGCONFIG(TAG, " White Channels Max Power: %u", this->max_power_white_channels_); + ESP_LOGCONFIG(TAG, + " Color Channels Max Power: %u\n" + " White Channels Max Power: %u", + this->max_power_color_channels_, this->max_power_white_channels_); } } // namespace sm2335 diff --git a/esphome/components/sml/sml.cpp b/esphome/components/sml/sml.cpp index 7ae9044c72..c1ffdc0e68 100644 --- a/esphome/components/sml/sml.cpp +++ b/esphome/components/sml/sml.cpp @@ -1,6 +1,6 @@ #include "sml.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "sml_parser.h" namespace esphome { diff --git a/esphome/components/sn74hc165/sn74hc165.cpp b/esphome/components/sn74hc165/sn74hc165.cpp index 7efe8a4c14..69e0df5785 100644 --- a/esphome/components/sn74hc165/sn74hc165.cpp +++ b/esphome/components/sn74hc165/sn74hc165.cpp @@ -7,8 +7,7 @@ namespace sn74hc165 { static const char *const TAG = "sn74hc165"; void SN74HC165Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up SN74HC165..."); - + ESP_LOGCONFIG(TAG, "Running setup"); // initialize pins this->clock_pin_->setup(); this->data_pin_->setup(); diff --git a/esphome/components/sn74hc595/sn74hc595.cpp b/esphome/components/sn74hc595/sn74hc595.cpp index 8a37c3bece..f8d24b898f 100644 --- a/esphome/components/sn74hc595/sn74hc595.cpp +++ b/esphome/components/sn74hc595/sn74hc595.cpp @@ -7,8 +7,7 @@ namespace sn74hc595 { static const char *const TAG = "sn74hc595"; void SN74HC595Component::pre_setup_() { - ESP_LOGCONFIG(TAG, "Setting up SN74HC595..."); - + ESP_LOGCONFIG(TAG, "Running setup"); if (this->have_oe_pin_) { // disable output this->oe_pin_->setup(); this->oe_pin_->digital_write(true); diff --git a/esphome/components/sntp/sntp_component.cpp b/esphome/components/sntp/sntp_component.cpp index 21add1451d..f9a9981c52 100644 --- a/esphome/components/sntp/sntp_component.cpp +++ b/esphome/components/sntp/sntp_component.cpp @@ -15,7 +15,7 @@ namespace sntp { static const char *const TAG = "sntp"; void SNTPComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up SNTP..."); + ESP_LOGCONFIG(TAG, "Running setup"); #if defined(USE_ESP_IDF) if (esp_sntp_enabled()) { esp_sntp_stop(); diff --git a/esphome/components/socket/__init__.py b/esphome/components/socket/__init__.py index 77e8fe51f6..667e30df4b 100644 --- a/esphome/components/socket/__init__.py +++ b/esphome/components/socket/__init__.py @@ -35,5 +35,7 @@ async def to_code(config): cg.add_define("USE_SOCKET_IMPL_LWIP_TCP") elif impl == IMPLEMENTATION_LWIP_SOCKETS: cg.add_define("USE_SOCKET_IMPL_LWIP_SOCKETS") + cg.add_define("USE_SOCKET_SELECT_SUPPORT") elif impl == IMPLEMENTATION_BSD_SOCKETS: cg.add_define("USE_SOCKET_IMPL_BSD_SOCKETS") + cg.add_define("USE_SOCKET_SELECT_SUPPORT") diff --git a/esphome/components/socket/bsd_sockets_impl.cpp b/esphome/components/socket/bsd_sockets_impl.cpp index 1b3916fcab..e056696bcf 100644 --- a/esphome/components/socket/bsd_sockets_impl.cpp +++ b/esphome/components/socket/bsd_sockets_impl.cpp @@ -5,6 +5,7 @@ #ifdef USE_SOCKET_IMPL_BSD_SOCKETS #include +#include "esphome/core/application.h" #ifdef USE_ESP32 #include @@ -40,7 +41,20 @@ std::string format_sockaddr(const struct sockaddr_storage &storage) { class BSDSocketImpl : public Socket { public: - BSDSocketImpl(int fd) : fd_(fd) {} + BSDSocketImpl(int fd, bool monitor_loop = false) : fd_(fd) { +#ifdef USE_SOCKET_SELECT_SUPPORT + // Register new socket with the application for select() if monitoring requested + if (monitor_loop && fd_ >= 0) { + // Only set loop_monitored_ to true if registration succeeds + loop_monitored_ = App.register_socket_fd(fd_); + } else { + loop_monitored_ = false; + } +#else + // Without select support, ignore monitor_loop parameter + (void) monitor_loop; +#endif + } ~BSDSocketImpl() override { if (!closed_) { close(); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall) @@ -48,16 +62,35 @@ class BSDSocketImpl : public Socket { } int connect(const struct sockaddr *addr, socklen_t addrlen) override { return ::connect(fd_, addr, addrlen); } std::unique_ptr accept(struct sockaddr *addr, socklen_t *addrlen) override { + return accept_impl_(addr, addrlen, false); + } + std::unique_ptr accept_loop_monitored(struct sockaddr *addr, socklen_t *addrlen) override { + return accept_impl_(addr, addrlen, true); + } + + private: + std::unique_ptr accept_impl_(struct sockaddr *addr, socklen_t *addrlen, bool loop_monitored) { int fd = ::accept(fd_, addr, addrlen); if (fd == -1) return {}; - return make_unique(fd); + return make_unique(fd, loop_monitored); } + + public: int bind(const struct sockaddr *addr, socklen_t addrlen) override { return ::bind(fd_, addr, addrlen); } int close() override { - int ret = ::close(fd_); - closed_ = true; - return ret; + if (!closed_) { +#ifdef USE_SOCKET_SELECT_SUPPORT + // Unregister from select() before closing if monitored + if (loop_monitored_) { + App.unregister_socket_fd(fd_); + } +#endif + int ret = ::close(fd_); + closed_ = true; + return ret; + } + return 0; } int shutdown(int how) override { return ::shutdown(fd_, how); } @@ -126,16 +159,27 @@ class BSDSocketImpl : public Socket { return 0; } + int get_fd() const override { return fd_; } + protected: int fd_; bool closed_ = false; }; -std::unique_ptr socket(int domain, int type, int protocol) { +// Helper to create a socket with optional monitoring +static std::unique_ptr create_socket(int domain, int type, int protocol, bool loop_monitored = false) { int ret = ::socket(domain, type, protocol); if (ret == -1) return nullptr; - return std::unique_ptr{new BSDSocketImpl(ret)}; + return std::unique_ptr{new BSDSocketImpl(ret, loop_monitored)}; +} + +std::unique_ptr socket(int domain, int type, int protocol) { + return create_socket(domain, type, protocol, false); +} + +std::unique_ptr socket_loop_monitored(int domain, int type, int protocol) { + return create_socket(domain, type, protocol, true); } } // namespace socket diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index 1d998902ff..2d64a275df 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -606,6 +606,11 @@ std::unique_ptr socket(int domain, int type, int protocol) { return std::unique_ptr{sock}; } +std::unique_ptr socket_loop_monitored(int domain, int type, int protocol) { + // LWIPRawImpl doesn't use file descriptors, so monitoring is not applicable + return socket(domain, type, protocol); +} + } // namespace socket } // namespace esphome diff --git a/esphome/components/socket/lwip_sockets_impl.cpp b/esphome/components/socket/lwip_sockets_impl.cpp index c41e42fc83..f8a1cbc046 100644 --- a/esphome/components/socket/lwip_sockets_impl.cpp +++ b/esphome/components/socket/lwip_sockets_impl.cpp @@ -5,6 +5,7 @@ #ifdef USE_SOCKET_IMPL_LWIP_SOCKETS #include +#include "esphome/core/application.h" namespace esphome { namespace socket { @@ -33,7 +34,20 @@ std::string format_sockaddr(const struct sockaddr_storage &storage) { class LwIPSocketImpl : public Socket { public: - LwIPSocketImpl(int fd) : fd_(fd) {} + LwIPSocketImpl(int fd, bool monitor_loop = false) : fd_(fd) { +#ifdef USE_SOCKET_SELECT_SUPPORT + // Register new socket with the application for select() if monitoring requested + if (monitor_loop && fd_ >= 0) { + // Only set loop_monitored_ to true if registration succeeds + loop_monitored_ = App.register_socket_fd(fd_); + } else { + loop_monitored_ = false; + } +#else + // Without select support, ignore monitor_loop parameter + (void) monitor_loop; +#endif + } ~LwIPSocketImpl() override { if (!closed_) { close(); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall) @@ -41,16 +55,35 @@ class LwIPSocketImpl : public Socket { } int connect(const struct sockaddr *addr, socklen_t addrlen) override { return lwip_connect(fd_, addr, addrlen); } std::unique_ptr accept(struct sockaddr *addr, socklen_t *addrlen) override { + return accept_impl_(addr, addrlen, false); + } + std::unique_ptr accept_loop_monitored(struct sockaddr *addr, socklen_t *addrlen) override { + return accept_impl_(addr, addrlen, true); + } + + private: + std::unique_ptr accept_impl_(struct sockaddr *addr, socklen_t *addrlen, bool loop_monitored) { int fd = lwip_accept(fd_, addr, addrlen); if (fd == -1) return {}; - return make_unique(fd); + return make_unique(fd, loop_monitored); } + + public: int bind(const struct sockaddr *addr, socklen_t addrlen) override { return lwip_bind(fd_, addr, addrlen); } int close() override { - int ret = lwip_close(fd_); - closed_ = true; - return ret; + if (!closed_) { +#ifdef USE_SOCKET_SELECT_SUPPORT + // Unregister from select() before closing if monitored + if (loop_monitored_) { + App.unregister_socket_fd(fd_); + } +#endif + int ret = lwip_close(fd_); + closed_ = true; + return ret; + } + return 0; } int shutdown(int how) override { return lwip_shutdown(fd_, how); } @@ -98,16 +131,27 @@ class LwIPSocketImpl : public Socket { return 0; } + int get_fd() const override { return fd_; } + protected: int fd_; bool closed_ = false; }; -std::unique_ptr socket(int domain, int type, int protocol) { +// Helper to create a socket with optional monitoring +static std::unique_ptr create_socket(int domain, int type, int protocol, bool loop_monitored = false) { int ret = lwip_socket(domain, type, protocol); if (ret == -1) return nullptr; - return std::unique_ptr{new LwIPSocketImpl(ret)}; + return std::unique_ptr{new LwIPSocketImpl(ret, loop_monitored)}; +} + +std::unique_ptr socket(int domain, int type, int protocol) { + return create_socket(domain, type, protocol, false); +} + +std::unique_ptr socket_loop_monitored(int domain, int type, int protocol) { + return create_socket(domain, type, protocol, true); } } // namespace socket diff --git a/esphome/components/socket/socket.cpp b/esphome/components/socket/socket.cpp index e260fce05e..1c8e72b8fd 100644 --- a/esphome/components/socket/socket.cpp +++ b/esphome/components/socket/socket.cpp @@ -4,12 +4,35 @@ #include #include #include "esphome/core/log.h" +#include "esphome/core/application.h" namespace esphome { namespace socket { Socket::~Socket() {} +bool Socket::ready() const { +#ifdef USE_SOCKET_SELECT_SUPPORT + if (!loop_monitored_) { + // Non-monitored sockets always return true (assume data may be available) + return true; + } + + // For loop-monitored sockets, check with the Application's select() results + int fd = this->get_fd(); + if (fd < 0) { + // No valid file descriptor, assume ready (fallback behavior) + return true; + } + + return App.is_socket_ready(fd); +#else + // Without select() support, we can't monitor sockets in the loop + // Always return true (assume data may be available) + return true; +#endif +} + std::unique_ptr socket_ip(int type, int protocol) { #if USE_NETWORK_IPV6 return socket(AF_INET6, type, protocol); @@ -18,6 +41,14 @@ std::unique_ptr socket_ip(int type, int protocol) { #endif /* USE_NETWORK_IPV6 */ } +std::unique_ptr socket_ip_loop_monitored(int type, int protocol) { +#if USE_NETWORK_IPV6 + return socket_loop_monitored(AF_INET6, type, protocol); +#else + return socket_loop_monitored(AF_INET, type, protocol); +#endif /* USE_NETWORK_IPV6 */ +} + socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port) { #if USE_NETWORK_IPV6 if (ip_address.find(':') != std::string::npos) { diff --git a/esphome/components/socket/socket.h b/esphome/components/socket/socket.h index 917f3c4c7f..8f0d28362e 100644 --- a/esphome/components/socket/socket.h +++ b/esphome/components/socket/socket.h @@ -17,6 +17,11 @@ class Socket { Socket &operator=(const Socket &) = delete; virtual std::unique_ptr accept(struct sockaddr *addr, socklen_t *addrlen) = 0; + /// Accept a connection and monitor it in the main loop + /// NOTE: This function is NOT thread-safe and must only be called from the main loop + virtual std::unique_ptr accept_loop_monitored(struct sockaddr *addr, socklen_t *addrlen) { + return accept(addr, addrlen); // Default implementation for backward compatibility + } virtual int bind(const struct sockaddr *addr, socklen_t addrlen) = 0; virtual int close() = 0; // not supported yet: @@ -44,14 +49,35 @@ class Socket { virtual int setblocking(bool blocking) = 0; virtual int loop() { return 0; }; + + /// Get the underlying file descriptor (returns -1 if not supported) + virtual int get_fd() const { return -1; } + + /// Check if socket has data ready to read + /// For loop-monitored sockets, checks with the Application's select() results + /// For non-monitored sockets, always returns true (assumes data may be available) + bool ready() const; + + protected: +#ifdef USE_SOCKET_SELECT_SUPPORT + bool loop_monitored_{false}; ///< Whether this socket is monitored by the event loop +#endif }; /// Create a socket of the given domain, type and protocol. std::unique_ptr socket(int domain, int type, int protocol); - /// Create a socket in the newest available IP domain (IPv6 or IPv4) of the given type and protocol. std::unique_ptr socket_ip(int type, int protocol); +/// Create a socket and monitor it for data in the main loop. +/// Like socket() but also registers the socket with the Application's select() loop. +/// WARNING: These functions are NOT thread-safe. They must only be called from the main loop +/// as they register the socket file descriptor with the global Application instance. +/// NOTE: On ESP platforms, FD_SETSIZE is typically 10, limiting the number of monitored sockets. +/// File descriptors >= FD_SETSIZE will not be monitored and will log an error. +std::unique_ptr socket_loop_monitored(int domain, int type, int protocol); +std::unique_ptr socket_ip_loop_monitored(int type, int protocol); + /// Set a sockaddr to the specified address and port for the IP version used by socket_ip(). socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port); diff --git a/esphome/components/sonoff_d1/sonoff_d1.cpp b/esphome/components/sonoff_d1/sonoff_d1.cpp index e70ec7b70d..e3d55681c5 100644 --- a/esphome/components/sonoff_d1/sonoff_d1.cpp +++ b/esphome/components/sonoff_d1/sonoff_d1.cpp @@ -286,10 +286,13 @@ void SonoffD1Output::write_state(light::LightState *state) { } void SonoffD1Output::dump_config() { - ESP_LOGCONFIG(TAG, "Sonoff D1 Dimmer: '%s'", this->light_state_ ? this->light_state_->get_name().c_str() : ""); - ESP_LOGCONFIG(TAG, " Use RM433 Remote: %s", ONOFF(this->use_rm433_remote_)); - ESP_LOGCONFIG(TAG, " Minimal brightness: %d", this->min_value_); - ESP_LOGCONFIG(TAG, " Maximal brightness: %d", this->max_value_); + ESP_LOGCONFIG(TAG, + "Sonoff D1 Dimmer: '%s'\n" + " Use RM433 Remote: %s\n" + " Minimal brightness: %d\n" + " Maximal brightness: %d", + this->light_state_ ? this->light_state_->get_name().c_str() : "", ONOFF(this->use_rm433_remote_), + this->min_value_, this->max_value_); } void SonoffD1Output::loop() { diff --git a/esphome/components/sonoff_d1/sonoff_d1.h b/esphome/components/sonoff_d1/sonoff_d1.h index 4df0f5afb2..19ff83f378 100644 --- a/esphome/components/sonoff_d1/sonoff_d1.h +++ b/esphome/components/sonoff_d1/sonoff_d1.h @@ -31,9 +31,9 @@ ----- */ -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/component.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/components/uart/uart.h" #include "esphome/components/light/light_output.h" #include "esphome/components/light/light_state.h" diff --git a/esphome/components/sound_level/sound_level.cpp b/esphome/components/sound_level/sound_level.cpp index f8447ce436..decf630aba 100644 --- a/esphome/components/sound_level/sound_level.cpp +++ b/esphome/components/sound_level/sound_level.cpp @@ -19,8 +19,10 @@ static const uint32_t RING_BUFFER_DURATION_MS = 120; static const double MAX_SAMPLE_SQUARED_DENOMINATOR = INT16_MIN * INT16_MIN; void SoundLevelComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Sound Level Component:"); - ESP_LOGCONFIG(TAG, " Measurement Duration: %" PRIu32 " ms", measurement_duration_ms_); + ESP_LOGCONFIG(TAG, + "Sound Level Component:\n" + " Measurement Duration: %" PRIu32 " ms", + measurement_duration_ms_); LOG_SENSOR(" ", "Peak:", this->peak_sensor_); LOG_SENSOR(" ", "RMS:", this->rms_sensor_); diff --git a/esphome/components/speaker/media_player/__init__.py b/esphome/components/speaker/media_player/__init__.py index cedafe214d..dc2dae2ef1 100644 --- a/esphome/components/speaker/media_player/__init__.py +++ b/esphome/components/speaker/media_player/__init__.py @@ -48,6 +48,7 @@ CONF_ON_UNMUTE = "on_unmute" CONF_ON_VOLUME = "on_volume" CONF_STREAM = "stream" CONF_VOLUME_INCREMENT = "volume_increment" +CONF_VOLUME_INITIAL = "volume_initial" CONF_VOLUME_MIN = "volume_min" CONF_VOLUME_MAX = "volume_max" @@ -282,6 +283,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_FILES): cv.ensure_list(MEDIA_FILE_TYPE_SCHEMA), cv.Optional(CONF_TASK_STACK_IN_PSRAM, default=False): cv.boolean, cv.Optional(CONF_VOLUME_INCREMENT, default=0.05): cv.percentage, + cv.Optional(CONF_VOLUME_INITIAL, default=0.5): cv.percentage, cv.Optional(CONF_VOLUME_MAX, default=1.0): cv.percentage, cv.Optional(CONF_VOLUME_MIN, default=0.0): cv.percentage, cv.Optional(CONF_ON_MUTE): automation.validate_automation(single=True), @@ -356,6 +358,7 @@ async def to_code(config): ) cg.add(var.set_volume_increment(config[CONF_VOLUME_INCREMENT])) + cg.add(var.set_volume_initial(config[CONF_VOLUME_INITIAL])) cg.add(var.set_volume_max(config[CONF_VOLUME_MAX])) cg.add(var.set_volume_min(config[CONF_VOLUME_MIN])) diff --git a/esphome/components/speaker/media_player/speaker_media_player.cpp b/esphome/components/speaker/media_player/speaker_media_player.cpp index fed0207c93..2c30f17c78 100644 --- a/esphome/components/speaker/media_player/speaker_media_player.cpp +++ b/esphome/components/speaker/media_player/speaker_media_player.cpp @@ -48,8 +48,6 @@ static const uint32_t MEDIA_CONTROLS_QUEUE_LENGTH = 20; static const UBaseType_t MEDIA_PIPELINE_TASK_PRIORITY = 1; static const UBaseType_t ANNOUNCEMENT_PIPELINE_TASK_PRIORITY = 1; -static const float FIRST_BOOT_DEFAULT_VOLUME = 0.5f; - static const char *const TAG = "speaker_media_player"; void SpeakerMediaPlayer::setup() { @@ -64,7 +62,7 @@ void SpeakerMediaPlayer::setup() { this->set_volume_(volume_restore_state.volume); this->set_mute_state_(volume_restore_state.is_muted); } else { - this->set_volume_(FIRST_BOOT_DEFAULT_VOLUME); + this->set_volume_(this->volume_initial_); this->set_mute_state_(false); } diff --git a/esphome/components/speaker/media_player/speaker_media_player.h b/esphome/components/speaker/media_player/speaker_media_player.h index 67e9859a13..967772d1a5 100644 --- a/esphome/components/speaker/media_player/speaker_media_player.h +++ b/esphome/components/speaker/media_player/speaker_media_player.h @@ -55,6 +55,9 @@ class SpeakerMediaPlayer : public Component, public media_player::MediaPlayer { // Percentage to increase or decrease the volume for volume up or volume down commands void set_volume_increment(float volume_increment) { this->volume_increment_ = volume_increment; } + // Volume used initially on first boot when no volume had been previously saved + void set_volume_initial(float volume_initial) { this->volume_initial_ = volume_initial; } + void set_volume_max(float volume_max) { this->volume_max_ = volume_max; } void set_volume_min(float volume_min) { this->volume_min_ = volume_min; } @@ -128,6 +131,9 @@ class SpeakerMediaPlayer : public Component, public media_player::MediaPlayer { // The amount to change the volume on volume up/down commands float volume_increment_; + // The initial volume used by Setup when no previous volume was saved + float volume_initial_; + float volume_max_; float volume_min_; diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index 5b28b3546b..ffb5e11f79 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -2,12 +2,14 @@ import re from esphome import pins import esphome.codegen as cg +from esphome.components.esp32 import only_on_variant from esphome.components.esp32.const import ( KEY_ESP32, VARIANT_ESP32C2, VARIANT_ESP32C3, VARIANT_ESP32C6, VARIANT_ESP32H2, + VARIANT_ESP32P4, VARIANT_ESP32S2, VARIANT_ESP32S3, ) @@ -287,7 +289,15 @@ def spi_mode_schema(mode): if mode == TYPE_SINGLE: return SPI_SINGLE_SCHEMA pin_count = 4 if mode == TYPE_QUAD else 8 + onlys = [cv.only_on([PLATFORM_ESP32]), cv.only_with_esp_idf] + if pin_count == 8: + onlys.append( + only_on_variant( + supported=[VARIANT_ESP32S3, VARIANT_ESP32S2, VARIANT_ESP32P4] + ) + ) return cv.All( + *onlys, cv.Schema( { cv.GenerateID(): cv.declare_id(TYPE_CLASS[mode]), @@ -308,8 +318,6 @@ def spi_mode_schema(mode): ), } ), - cv.only_on([PLATFORM_ESP32]), - cv.only_with_esp_idf, ) diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index 18f7852757..76d9d8ae86 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -18,7 +18,7 @@ GPIOPin *const NullPin::NULL_PIN = new NullPin(); // NOLINT(cppcoreguidelines-a SPIDelegate *SPIComponent::register_device(SPIClient *device, SPIMode mode, SPIBitOrder bit_order, uint32_t data_rate, GPIOPin *cs_pin) { if (this->devices_.count(device) != 0) { - ESP_LOGE(TAG, "SPI device already registered"); + ESP_LOGE(TAG, "Device already registered"); return this->devices_[device]; } SPIDelegate *delegate = this->spi_bus_->get_delegate(data_rate, bit_order, mode, cs_pin); // NOLINT @@ -28,7 +28,7 @@ SPIDelegate *SPIComponent::register_device(SPIClient *device, SPIMode mode, SPIB void SPIComponent::unregister_device(SPIClient *device) { if (this->devices_.count(device) == 0) { - esph_log_e(TAG, "SPI device not registered"); + esph_log_e(TAG, "Device not registered"); return; } delete this->devices_[device]; // NOLINT @@ -36,14 +36,14 @@ void SPIComponent::unregister_device(SPIClient *device) { } void SPIComponent::setup() { - ESP_LOGD(TAG, "Setting up SPI bus..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (this->sdo_pin_ == nullptr) this->sdo_pin_ = NullPin::NULL_PIN; if (this->sdi_pin_ == nullptr) this->sdi_pin_ = NullPin::NULL_PIN; if (this->clk_pin_ == nullptr) { - ESP_LOGE(TAG, "No clock pin for SPI"); + ESP_LOGE(TAG, "No clock pin"); this->mark_failed(); return; } diff --git a/esphome/components/spi/spi_arduino.cpp b/esphome/components/spi/spi_arduino.cpp index f7fe523a33..432f7cf2cd 100644 --- a/esphome/components/spi/spi_arduino.cpp +++ b/esphome/components/spi/spi_arduino.cpp @@ -3,7 +3,6 @@ namespace esphome { namespace spi { - #ifdef USE_ARDUINO static const char *const TAG = "spi-esp-arduino"; @@ -38,17 +37,31 @@ class SPIDelegateHw : public SPIDelegate { void write16(uint16_t data) override { this->channel_->transfer16(data); } -#ifdef USE_RP2040 void write_array(const uint8_t *ptr, size_t length) override { - // avoid overwriting the supplied buffer - uint8_t *rxbuf = new uint8_t[length]; // NOLINT(cppcoreguidelines-owning-memory) - memcpy(rxbuf, ptr, length); - this->channel_->transfer((void *) rxbuf, length); - delete[] rxbuf; // NOLINT(cppcoreguidelines-owning-memory) - } + if (length == 1) { + this->channel_->transfer(*ptr); + return; + } +#ifdef USE_RP2040 + // avoid overwriting the supplied buffer. Use vector for automatic deallocation + auto rxbuf = std::vector(length); + memcpy(rxbuf.data(), ptr, length); + this->channel_->transfer((void *) rxbuf.data(), length); +#elif defined(USE_ESP8266) + // ESP8266 SPI library requires the pointer to be word aligned, but the data may not be + // so we need to copy the data to a temporary buffer + if (reinterpret_cast(ptr) & 0x3) { + ESP_LOGVV(TAG, "SPI write buffer not word aligned, copying to temporary buffer"); + auto txbuf = std::vector(length); + memcpy(txbuf.data(), ptr, length); + this->channel_->writeBytes(txbuf.data(), length); + } else { + this->channel_->writeBytes(ptr, length); + } #else - void write_array(const uint8_t *ptr, size_t length) override { this->channel_->writeBytes(ptr, length); } + this->channel_->writeBytes(ptr, length); #endif + } void read_array(uint8_t *ptr, size_t length) override { this->channel_->transfer(ptr, length); } diff --git a/esphome/components/spi_device/spi_device.cpp b/esphome/components/spi_device/spi_device.cpp index 1f579cb802..872b3054e6 100644 --- a/esphome/components/spi_device/spi_device.cpp +++ b/esphome/components/spi_device/spi_device.cpp @@ -9,9 +9,8 @@ namespace spi_device { static const char *const TAG = "spi_device"; void SPIDeviceComponent::setup() { - ESP_LOGD(TAG, "Setting up SPIDevice..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); - ESP_LOGCONFIG(TAG, "SPIDevice started!"); } void SPIDeviceComponent::dump_config() { diff --git a/esphome/components/sprinkler/sprinkler.cpp b/esphome/components/sprinkler/sprinkler.cpp index 50ea3eff51..e191498857 100644 --- a/esphome/components/sprinkler/sprinkler.cpp +++ b/esphome/components/sprinkler/sprinkler.cpp @@ -1712,15 +1712,18 @@ void Sprinkler::dump_config() { if (this->valve_overlap_) { ESP_LOGCONFIG(TAG, " Valve Overlap: %" PRIu32 " seconds", this->switching_delay_.value_or(0)); } else { - ESP_LOGCONFIG(TAG, " Valve Open Delay: %" PRIu32 " seconds", this->switching_delay_.value_or(0)); - ESP_LOGCONFIG(TAG, " Pump Switch Off During Valve Open Delay: %s", - YESNO(this->pump_switch_off_during_valve_open_delay_)); + ESP_LOGCONFIG(TAG, + " Valve Open Delay: %" PRIu32 " seconds\n" + " Pump Switch Off During Valve Open Delay: %s", + this->switching_delay_.value_or(0), YESNO(this->pump_switch_off_during_valve_open_delay_)); } } for (size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) { - ESP_LOGCONFIG(TAG, " Valve %zu:", valve_number); - ESP_LOGCONFIG(TAG, " Name: %s", this->valve_name(valve_number)); - ESP_LOGCONFIG(TAG, " Run Duration: %" PRIu32 " seconds", this->valve_run_duration(valve_number)); + ESP_LOGCONFIG(TAG, + " Valve %zu:\n" + " Name: %s\n" + " Run Duration: %" PRIu32 " seconds", + valve_number, this->valve_name(valve_number), this->valve_run_duration(valve_number)); if (this->valve_[valve_number].valve_switch.pulse_duration()) { ESP_LOGCONFIG(TAG, " Pulse Duration: %" PRIu32 " milliseconds", this->valve_[valve_number].valve_switch.pulse_duration()); diff --git a/esphome/components/sps30/sps30.cpp b/esphome/components/sps30/sps30.cpp index cdcd4a6a54..c0df539867 100644 --- a/esphome/components/sps30/sps30.cpp +++ b/esphome/components/sps30/sps30.cpp @@ -22,7 +22,7 @@ static const size_t SERIAL_NUMBER_LENGTH = 8; static const uint8_t MAX_SKIPPED_DATA_CYCLES_BEFORE_ERROR = 5; void SPS30Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up sps30..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->write_command(SPS30_CMD_SOFT_RESET); /// Deferred Sensor initialization this->set_timeout(500, [this]() { @@ -73,10 +73,10 @@ void SPS30Component::dump_config() { if (this->is_failed()) { switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGW(TAG, "Communication failed! Is the sensor connected?"); + ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); break; case MEASUREMENT_INIT_FAILED: - ESP_LOGW(TAG, "Measurement Initialization failed!"); + ESP_LOGW(TAG, "Measurement Initialization failed"); break; case SERIAL_NUMBER_REQUEST_FAILED: ESP_LOGW(TAG, "Unable to request sensor serial number"); @@ -91,14 +91,15 @@ void SPS30Component::dump_config() { ESP_LOGW(TAG, "Unable to read sensor firmware version"); break; default: - ESP_LOGW(TAG, "Unknown setup error!"); + ESP_LOGW(TAG, "Unknown setup error"); break; } } LOG_UPDATE_INTERVAL(this); - ESP_LOGCONFIG(TAG, " Serial Number: '%s'", this->serial_number_); - ESP_LOGCONFIG(TAG, " Firmware version v%0d.%0d", (raw_firmware_version_ >> 8), - uint16_t(raw_firmware_version_ & 0xFF)); + ESP_LOGCONFIG(TAG, + " Serial Number: '%s'\n" + " Firmware version v%0d.%0d", + this->serial_number_, (raw_firmware_version_ >> 8), uint16_t(raw_firmware_version_ & 0xFF)); LOG_SENSOR(" ", "PM1.0 Weight Concentration", this->pm_1_0_sensor_); LOG_SENSOR(" ", "PM2.5 Weight Concentration", this->pm_2_5_sensor_); LOG_SENSOR(" ", "PM4 Weight Concentration", this->pm_4_0_sensor_); @@ -113,18 +114,18 @@ void SPS30Component::dump_config() { void SPS30Component::update() { /// Check if warning flag active (sensor reconnected?) if (this->status_has_warning()) { - ESP_LOGD(TAG, "Trying to reconnect the sensor..."); + ESP_LOGD(TAG, "Trying to reconnect"); if (this->write_command(SPS30_CMD_SOFT_RESET)) { - ESP_LOGD(TAG, "Sensor has soft-reset successfully. Waiting for reconnection in 500ms..."); + ESP_LOGD(TAG, "Soft-reset successful. Waiting for reconnection in 500 ms"); this->set_timeout(500, [this]() { this->start_continuous_measurement_(); /// Sensor restarted and reading attempt made next cycle this->status_clear_warning(); this->skipped_data_read_cycles_ = 0; - ESP_LOGD(TAG, "Sensor reconnected successfully. Resuming continuous measurement!"); + ESP_LOGD(TAG, "Reconnect successful. Resuming continuous measurement"); }); } else { - ESP_LOGD(TAG, "Sensor soft-reset failed. Is the sensor offline?"); + ESP_LOGD(TAG, "Soft-reset failed"); } return; } @@ -136,19 +137,19 @@ void SPS30Component::update() { uint16_t raw_read_status; if (!this->read_data(&raw_read_status, 1) || raw_read_status == 0x00) { - ESP_LOGD(TAG, "Sensor measurement not ready yet."); + ESP_LOGD(TAG, "Not ready yet"); this->skipped_data_read_cycles_++; /// The following logic is required to address the cases when a sensor is quickly replaced before it's marked /// as failed so that new sensor is eventually forced to be reinitialized for continuous measurement. if (this->skipped_data_read_cycles_ > MAX_SKIPPED_DATA_CYCLES_BEFORE_ERROR) { - ESP_LOGD(TAG, "Sensor exceeded max allowed attempts. Sensor communication will be reinitialized."); + ESP_LOGD(TAG, "Exceeded max allowed attempts; communication will be reinitialized"); this->status_set_warning(); } return; } if (!this->write_command(SPS30_CMD_READ_MEASUREMENT)) { - ESP_LOGW(TAG, "Error reading measurement status!"); + ESP_LOGW(TAG, "Error reading status"); this->status_set_warning(); return; } @@ -156,7 +157,7 @@ void SPS30Component::update() { this->set_timeout(50, [this]() { uint16_t raw_data[20]; if (!this->read_data(raw_data, 20)) { - ESP_LOGW(TAG, "Error reading measurement data!"); + ESP_LOGW(TAG, "Error reading data"); this->status_set_warning(); return; } diff --git a/esphome/components/ssd1306_base/ssd1306_base.cpp b/esphome/components/ssd1306_base/ssd1306_base.cpp index 90b805a79f..0547a77184 100644 --- a/esphome/components/ssd1306_base/ssd1306_base.cpp +++ b/esphome/components/ssd1306_base/ssd1306_base.cpp @@ -1,6 +1,6 @@ #include "ssd1306_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ssd1306_base { diff --git a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp index ed7cf102ee..f9a2609948 100644 --- a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +++ b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp @@ -7,7 +7,7 @@ namespace ssd1306_i2c { static const char *const TAG = "ssd1306_i2c"; void I2CSSD1306::setup() { - ESP_LOGCONFIG(TAG, "Setting up I2C SSD1306..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->init_reset_(); auto err = this->write(nullptr, 0); @@ -24,16 +24,19 @@ void I2CSSD1306::dump_config() { LOG_I2C_DEVICE(this); ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " External VCC: %s", YESNO(this->external_vcc_)); - ESP_LOGCONFIG(TAG, " Flip X: %s", YESNO(this->flip_x_)); - ESP_LOGCONFIG(TAG, " Flip Y: %s", YESNO(this->flip_y_)); - ESP_LOGCONFIG(TAG, " Offset X: %d", this->offset_x_); - ESP_LOGCONFIG(TAG, " Offset Y: %d", this->offset_y_); - ESP_LOGCONFIG(TAG, " Inverted Color: %s", YESNO(this->invert_)); + ESP_LOGCONFIG(TAG, + " External VCC: %s\n" + " Flip X: %s\n" + " Flip Y: %s\n" + " Offset X: %d\n" + " Offset Y: %d\n" + " Inverted Color: %s", + YESNO(this->external_vcc_), YESNO(this->flip_x_), YESNO(this->flip_y_), this->offset_x_, + this->offset_y_, YESNO(this->invert_)); LOG_UPDATE_INTERVAL(this); if (this->error_code_ == COMMUNICATION_FAILED) { - ESP_LOGE(TAG, "Communication with SSD1306 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } void I2CSSD1306::command(uint8_t value) { this->write_byte(0x00, value); } diff --git a/esphome/components/ssd1306_spi/ssd1306_spi.cpp b/esphome/components/ssd1306_spi/ssd1306_spi.cpp index 0a0debfd65..249e6593ae 100644 --- a/esphome/components/ssd1306_spi/ssd1306_spi.cpp +++ b/esphome/components/ssd1306_spi/ssd1306_spi.cpp @@ -8,7 +8,7 @@ namespace ssd1306_spi { static const char *const TAG = "ssd1306_spi"; void SPISSD1306::setup() { - ESP_LOGCONFIG(TAG, "Setting up SPI SSD1306..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); this->dc_pin_->setup(); // OUTPUT @@ -21,12 +21,15 @@ void SPISSD1306::dump_config() { LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " External VCC: %s", YESNO(this->external_vcc_)); - ESP_LOGCONFIG(TAG, " Flip X: %s", YESNO(this->flip_x_)); - ESP_LOGCONFIG(TAG, " Flip Y: %s", YESNO(this->flip_y_)); - ESP_LOGCONFIG(TAG, " Offset X: %d", this->offset_x_); - ESP_LOGCONFIG(TAG, " Offset Y: %d", this->offset_y_); - ESP_LOGCONFIG(TAG, " Inverted Color: %s", YESNO(this->invert_)); + ESP_LOGCONFIG(TAG, + " External VCC: %s\n" + " Flip X: %s\n" + " Flip Y: %s\n" + " Offset X: %d\n" + " Offset Y: %d\n" + " Inverted Color: %s", + YESNO(this->external_vcc_), YESNO(this->flip_x_), YESNO(this->flip_y_), this->offset_x_, + this->offset_y_, YESNO(this->invert_)); LOG_UPDATE_INTERVAL(this); } void SPISSD1306::command(uint8_t value) { diff --git a/esphome/components/ssd1322_base/ssd1322_base.cpp b/esphome/components/ssd1322_base/ssd1322_base.cpp index 520248a66e..eb8d87998f 100644 --- a/esphome/components/ssd1322_base/ssd1322_base.cpp +++ b/esphome/components/ssd1322_base/ssd1322_base.cpp @@ -1,6 +1,6 @@ #include "ssd1322_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ssd1322_base { diff --git a/esphome/components/ssd1322_spi/ssd1322_spi.cpp b/esphome/components/ssd1322_spi/ssd1322_spi.cpp index a841c5606e..fb2d8afe1c 100644 --- a/esphome/components/ssd1322_spi/ssd1322_spi.cpp +++ b/esphome/components/ssd1322_spi/ssd1322_spi.cpp @@ -8,7 +8,7 @@ namespace ssd1322_spi { static const char *const TAG = "ssd1322_spi"; void SPISSD1322::setup() { - ESP_LOGCONFIG(TAG, "Setting up SPI SSD1322..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); this->dc_pin_->setup(); // OUTPUT if (this->cs_) diff --git a/esphome/components/ssd1325_base/ssd1325_base.cpp b/esphome/components/ssd1325_base/ssd1325_base.cpp index 711f9e5197..e7d2386ac7 100644 --- a/esphome/components/ssd1325_base/ssd1325_base.cpp +++ b/esphome/components/ssd1325_base/ssd1325_base.cpp @@ -1,6 +1,6 @@ #include "ssd1325_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ssd1325_base { diff --git a/esphome/components/ssd1325_spi/ssd1325_spi.cpp b/esphome/components/ssd1325_spi/ssd1325_spi.cpp index 8a95bfeae3..d2a365326f 100644 --- a/esphome/components/ssd1325_spi/ssd1325_spi.cpp +++ b/esphome/components/ssd1325_spi/ssd1325_spi.cpp @@ -8,7 +8,7 @@ namespace ssd1325_spi { static const char *const TAG = "ssd1325_spi"; void SPISSD1325::setup() { - ESP_LOGCONFIG(TAG, "Setting up SPI SSD1325..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); this->dc_pin_->setup(); // OUTPUT if (this->cs_) diff --git a/esphome/components/ssd1327_base/ssd1327_base.cpp b/esphome/components/ssd1327_base/ssd1327_base.cpp index 4223a013a4..6b83ec5f9d 100644 --- a/esphome/components/ssd1327_base/ssd1327_base.cpp +++ b/esphome/components/ssd1327_base/ssd1327_base.cpp @@ -1,6 +1,6 @@ #include "ssd1327_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ssd1327_base { diff --git a/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp b/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp index e9e047bfb6..4e1c5e4ea0 100644 --- a/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp +++ b/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp @@ -7,7 +7,7 @@ namespace ssd1327_i2c { static const char *const TAG = "ssd1327_i2c"; void I2CSSD1327::setup() { - ESP_LOGCONFIG(TAG, "Setting up I2C SSD1327..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->init_reset_(); auto err = this->write(nullptr, 0); @@ -27,7 +27,7 @@ void I2CSSD1327::dump_config() { LOG_UPDATE_INTERVAL(this); if (this->error_code_ == COMMUNICATION_FAILED) { - ESP_LOGE(TAG, "Communication with SSD1327 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } void I2CSSD1327::command(uint8_t value) { this->write_byte(0x00, value); } diff --git a/esphome/components/ssd1327_spi/ssd1327_spi.cpp b/esphome/components/ssd1327_spi/ssd1327_spi.cpp index c6ae377119..a5eaf252c4 100644 --- a/esphome/components/ssd1327_spi/ssd1327_spi.cpp +++ b/esphome/components/ssd1327_spi/ssd1327_spi.cpp @@ -8,7 +8,7 @@ namespace ssd1327_spi { static const char *const TAG = "ssd1327_spi"; void SPISSD1327::setup() { - ESP_LOGCONFIG(TAG, "Setting up SPI SSD1327..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); this->dc_pin_->setup(); // OUTPUT if (this->cs_) diff --git a/esphome/components/ssd1331_base/ssd1331_base.cpp b/esphome/components/ssd1331_base/ssd1331_base.cpp index 14f94dee4f..8ee12387e4 100644 --- a/esphome/components/ssd1331_base/ssd1331_base.cpp +++ b/esphome/components/ssd1331_base/ssd1331_base.cpp @@ -1,6 +1,6 @@ #include "ssd1331_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ssd1331_base { diff --git a/esphome/components/ssd1331_spi/ssd1331_spi.cpp b/esphome/components/ssd1331_spi/ssd1331_spi.cpp index 88116f6c00..aeff2bbbfd 100644 --- a/esphome/components/ssd1331_spi/ssd1331_spi.cpp +++ b/esphome/components/ssd1331_spi/ssd1331_spi.cpp @@ -8,7 +8,7 @@ namespace ssd1331_spi { static const char *const TAG = "ssd1331_spi"; void SPISSD1331::setup() { - ESP_LOGCONFIG(TAG, "Setting up SPI SSD1331..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); this->dc_pin_->setup(); // OUTPUT if (this->cs_) diff --git a/esphome/components/ssd1351_base/ssd1351_base.cpp b/esphome/components/ssd1351_base/ssd1351_base.cpp index 38ea05a0b8..09530e8a27 100644 --- a/esphome/components/ssd1351_base/ssd1351_base.cpp +++ b/esphome/components/ssd1351_base/ssd1351_base.cpp @@ -1,6 +1,6 @@ #include "ssd1351_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace ssd1351_base { diff --git a/esphome/components/ssd1351_spi/ssd1351_spi.cpp b/esphome/components/ssd1351_spi/ssd1351_spi.cpp index b71b8f4f88..5ae7c308d4 100644 --- a/esphome/components/ssd1351_spi/ssd1351_spi.cpp +++ b/esphome/components/ssd1351_spi/ssd1351_spi.cpp @@ -8,7 +8,7 @@ namespace ssd1351_spi { static const char *const TAG = "ssd1351_spi"; void SPISSD1351::setup() { - ESP_LOGCONFIG(TAG, "Setting up SPI SSD1351..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); this->dc_pin_->setup(); // OUTPUT if (this->cs_) diff --git a/esphome/components/st7567_base/st7567_base.cpp b/esphome/components/st7567_base/st7567_base.cpp index b22a7d7fd5..0afd2a70ba 100644 --- a/esphome/components/st7567_base/st7567_base.cpp +++ b/esphome/components/st7567_base/st7567_base.cpp @@ -1,6 +1,6 @@ #include "st7567_base.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace st7567_base { @@ -13,7 +13,7 @@ void ST7567::setup() { } void ST7567::display_init_() { - ESP_LOGD(TAG, "Initializing ST7567 display..."); + ESP_LOGD(TAG, "Initializing display"); this->display_init_registers_(); this->clear(); this->write_display_data(); @@ -42,7 +42,7 @@ void ST7567::display_init_registers_() { } void ST7567::display_sw_refresh_() { - ESP_LOGD(TAG, "Performing refresh sequence..."); + ESP_LOGD(TAG, "Performing refresh sequence"); this->command(ST7567_SW_REFRESH); this->display_init_registers_(); } diff --git a/esphome/components/st7567_i2c/st7567_i2c.cpp b/esphome/components/st7567_i2c/st7567_i2c.cpp index 05173d1be5..0640d3be8d 100644 --- a/esphome/components/st7567_i2c/st7567_i2c.cpp +++ b/esphome/components/st7567_i2c/st7567_i2c.cpp @@ -7,7 +7,7 @@ namespace st7567_i2c { static const char *const TAG = "st7567_i2c"; void I2CST7567::setup() { - ESP_LOGCONFIG(TAG, "Setting up I2C ST7567 display..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->init_reset_(); auto err = this->write(nullptr, 0); @@ -24,13 +24,15 @@ void I2CST7567::dump_config() { LOG_I2C_DEVICE(this); ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " Mirror X: %s", YESNO(this->mirror_x_)); - ESP_LOGCONFIG(TAG, " Mirror Y: %s", YESNO(this->mirror_y_)); - ESP_LOGCONFIG(TAG, " Invert Colors: %s", YESNO(this->invert_colors_)); + ESP_LOGCONFIG(TAG, + " Mirror X: %s\n" + " Mirror Y: %s\n" + " Invert Colors: %s", + YESNO(this->mirror_x_), YESNO(this->mirror_y_), YESNO(this->invert_colors_)); LOG_UPDATE_INTERVAL(this); if (this->error_code_ == COMMUNICATION_FAILED) { - ESP_LOGE(TAG, "Communication with I2C ST7567 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } diff --git a/esphome/components/st7567_spi/st7567_spi.cpp b/esphome/components/st7567_spi/st7567_spi.cpp index 25698d0dd0..c5c5836200 100644 --- a/esphome/components/st7567_spi/st7567_spi.cpp +++ b/esphome/components/st7567_spi/st7567_spi.cpp @@ -7,7 +7,7 @@ namespace st7567_spi { static const char *const TAG = "st7567_spi"; void SPIST7567::setup() { - ESP_LOGCONFIG(TAG, "Setting up SPI ST7567 display..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); this->dc_pin_->setup(); if (this->cs_) diff --git a/esphome/components/st7701s/st7701s.cpp b/esphome/components/st7701s/st7701s.cpp index 403bff789d..6261f33b77 100644 --- a/esphome/components/st7701s/st7701s.cpp +++ b/esphome/components/st7701s/st7701s.cpp @@ -181,8 +181,10 @@ void ST7701S::write_init_sequence_() { void ST7701S::dump_config() { ESP_LOGCONFIG("", "ST7701S RGB LCD"); - ESP_LOGCONFIG(TAG, " Height: %u", this->height_); - ESP_LOGCONFIG(TAG, " Width: %u", this->width_); + ESP_LOGCONFIG(TAG, + " Height: %u\n" + " Width: %u", + this->height_, this->width_); LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" DE Pin: ", this->de_pin_); diff --git a/esphome/components/st7735/st7735.cpp b/esphome/components/st7735/st7735.cpp index 5985d8bfb3..9c9c0a3df5 100644 --- a/esphome/components/st7735/st7735.cpp +++ b/esphome/components/st7735/st7735.cpp @@ -1,7 +1,7 @@ #include "st7735.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace st7735 { @@ -233,7 +233,7 @@ ST7735::ST7735(ST7735Model model, int width, int height, int colstart, int rowst height_(height) {} void ST7735::setup() { - ESP_LOGCONFIG(TAG, "Setting up ST7735..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->spi_setup(); this->dc_pin_->setup(); // OUTPUT diff --git a/esphome/components/st7789v/st7789v.cpp b/esphome/components/st7789v/st7789v.cpp index 0d2f35aef5..1f3cd50d6c 100644 --- a/esphome/components/st7789v/st7789v.cpp +++ b/esphome/components/st7789v/st7789v.cpp @@ -8,7 +8,7 @@ static const char *const TAG = "st7789v"; static const size_t TEMP_BUFFER_SIZE = 128; void ST7789V::setup() { - ESP_LOGCONFIG(TAG, "Setting up SPI ST7789V..."); + ESP_LOGCONFIG(TAG, "Running setup"); #ifdef USE_POWER_SUPPLY this->power_.request(); // the PowerSupply component takes care of post turn-on delay @@ -122,12 +122,15 @@ void ST7789V::setup() { void ST7789V::dump_config() { LOG_DISPLAY("", "SPI ST7789V", this); - ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_); - ESP_LOGCONFIG(TAG, " Height: %u", this->height_); - ESP_LOGCONFIG(TAG, " Width: %u", this->width_); - ESP_LOGCONFIG(TAG, " Height Offset: %u", this->offset_height_); - ESP_LOGCONFIG(TAG, " Width Offset: %u", this->offset_width_); - ESP_LOGCONFIG(TAG, " 8-bit color mode: %s", YESNO(this->eightbitcolor_)); + ESP_LOGCONFIG(TAG, + " Model: %s\n" + " Height: %u\n" + " Width: %u\n" + " Height Offset: %u\n" + " Width Offset: %u\n" + " 8-bit color mode: %s", + this->model_str_, this->height_, this->width_, this->offset_height_, this->offset_width_, + YESNO(this->eightbitcolor_)); LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); diff --git a/esphome/components/st7920/st7920.cpp b/esphome/components/st7920/st7920.cpp index 171e7095dd..54ac6d2efd 100644 --- a/esphome/components/st7920/st7920.cpp +++ b/esphome/components/st7920/st7920.cpp @@ -32,7 +32,7 @@ static const uint8_t LCD_LINE2 = 0x88; static const uint8_t LCD_LINE3 = 0x98; void ST7920::setup() { - ESP_LOGCONFIG(TAG, "Setting up ST7920..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->dump_config(); this->spi_setup(); this->init_internal_(this->get_buffer_length_()); @@ -95,8 +95,10 @@ void ST7920::fill(Color color) { memset(this->buffer_, color.is_on() ? 0xFF : 0x void ST7920::dump_config() { LOG_DISPLAY("", "ST7920", this); LOG_PIN(" CS Pin: ", this->cs_); - ESP_LOGCONFIG(TAG, " Height: %d", this->height_); - ESP_LOGCONFIG(TAG, " Width: %d", this->width_); + ESP_LOGCONFIG(TAG, + " Height: %d\n" + " Width: %d", + this->height_, this->width_); } float ST7920::get_setup_priority() const { return setup_priority::PROCESSOR; } @@ -129,7 +131,7 @@ void HOT ST7920::draw_absolute_pixel_internal(int x, int y, Color color) { } void ST7920::display_init_() { - ESP_LOGD(TAG, "Initializing display..."); + ESP_LOGD(TAG, "Initializing display"); this->command_(LCD_BASIC); // 8bit mode this->command_(LCD_BASIC); // 8bit mode this->command_(LCD_CLS); // clear screen diff --git a/esphome/components/statsd/statsd.cpp b/esphome/components/statsd/statsd.cpp index b7fad19332..05f71c7b24 100644 --- a/esphome/components/statsd/statsd.cpp +++ b/esphome/components/statsd/statsd.cpp @@ -38,17 +38,21 @@ StatsdComponent::~StatsdComponent() { } void StatsdComponent::dump_config() { - ESP_LOGCONFIG(TAG, "statsD:"); - ESP_LOGCONFIG(TAG, " host: %s", this->host_); - ESP_LOGCONFIG(TAG, " port: %d", this->port_); + ESP_LOGCONFIG(TAG, + "statsD:\n" + " host: %s\n" + " port: %d", + this->host_, this->port_); if (this->prefix_) { ESP_LOGCONFIG(TAG, " prefix: %s", this->prefix_); } ESP_LOGCONFIG(TAG, " metrics:"); for (sensors_t s : this->sensors_) { - ESP_LOGCONFIG(TAG, " - name: %s", s.name); - ESP_LOGCONFIG(TAG, " type: %d", s.type); + ESP_LOGCONFIG(TAG, + " - name: %s\n" + " type: %d", + s.name, s.type); } } diff --git a/esphome/components/status_led/light/status_led_light.cpp b/esphome/components/status_led/light/status_led_light.cpp index 549024c4df..dc4820f6da 100644 --- a/esphome/components/status_led/light/status_led_light.cpp +++ b/esphome/components/status_led/light/status_led_light.cpp @@ -9,10 +9,10 @@ namespace status_led { static const char *const TAG = "status_led"; void StatusLEDLightOutput::loop() { - uint32_t new_state = App.get_app_state() & STATUS_LED_MASK; + uint8_t new_state = App.get_app_state() & STATUS_LED_MASK; if (new_state != this->last_app_state_) { - ESP_LOGV(TAG, "New app state 0x%08" PRIX32, new_state); + ESP_LOGV(TAG, "New app state 0x%02X", new_state); } if ((new_state & STATUS_LED_ERROR) != 0u) { @@ -53,7 +53,7 @@ void StatusLEDLightOutput::write_state(light::LightState *state) { } void StatusLEDLightOutput::setup() { - ESP_LOGCONFIG(TAG, "Setting up Status LED..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (this->pin_ != nullptr) { this->pin_->setup(); diff --git a/esphome/components/status_led/light/status_led_light.h b/esphome/components/status_led/light/status_led_light.h index e711a2e749..bfa144526a 100644 --- a/esphome/components/status_led/light/status_led_light.h +++ b/esphome/components/status_led/light/status_led_light.h @@ -36,7 +36,7 @@ class StatusLEDLightOutput : public light::LightOutput, public Component { GPIOPin *pin_{nullptr}; output::BinaryOutput *output_{nullptr}; light::LightState *lightstate_{}; - uint32_t last_app_state_{0xFFFF}; + uint8_t last_app_state_{0xFF}; void output_state_(bool state); }; diff --git a/esphome/components/status_led/status_led.cpp b/esphome/components/status_led/status_led.cpp index d2d56cf05b..a17d4398fd 100644 --- a/esphome/components/status_led/status_led.cpp +++ b/esphome/components/status_led/status_led.cpp @@ -11,7 +11,7 @@ StatusLED *global_status_led = nullptr; // NOLINT(cppcoreguidelines-avoid-non-c StatusLED::StatusLED(GPIOPin *pin) : pin_(pin) { global_status_led = this; } void StatusLED::pre_setup() { - ESP_LOGCONFIG(TAG, "Setting up Status LED..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->pin_->setup(); this->pin_->digital_write(false); } diff --git a/esphome/components/stepper/stepper.h b/esphome/components/stepper/stepper.h index ba2b3182d7..1cf4830b1f 100644 --- a/esphome/components/stepper/stepper.h +++ b/esphome/components/stepper/stepper.h @@ -7,9 +7,11 @@ namespace esphome { namespace stepper { #define LOG_STEPPER(this) \ - ESP_LOGCONFIG(TAG, " Acceleration: %.0f steps/s^2", this->acceleration_); \ - ESP_LOGCONFIG(TAG, " Deceleration: %.0f steps/s^2", this->deceleration_); \ - ESP_LOGCONFIG(TAG, " Max Speed: %.0f steps/s", this->max_speed_); + ESP_LOGCONFIG(TAG, \ + " Acceleration: %.0f steps/s^2\n" \ + " Deceleration: %.0f steps/s^2\n" \ + " Max Speed: %.0f steps/s", \ + this->acceleration_, this->deceleration_, this->max_speed_); class Stepper { public: diff --git a/esphome/components/sts3x/sts3x.cpp b/esphome/components/sts3x/sts3x.cpp index a533bc1d87..29aac24e90 100644 --- a/esphome/components/sts3x/sts3x.cpp +++ b/esphome/components/sts3x/sts3x.cpp @@ -18,7 +18,7 @@ static const uint16_t STS3X_COMMAND_HEATER_DISABLE = 0x3066; static const uint16_t STS3X_COMMAND_FETCH_DATA = 0xE000; void STS3XComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up STS3x..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->write_command(STS3X_COMMAND_READ_SERIAL_NUMBER)) { this->mark_failed(); return; @@ -36,7 +36,7 @@ void STS3XComponent::dump_config() { ESP_LOGCONFIG(TAG, "STS3x:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with ST3x failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/switch/__init__.py b/esphome/components/switch/__init__.py index e7445051e0..0211c648fc 100644 --- a/esphome/components/switch/__init__.py +++ b/esphome/components/switch/__init__.py @@ -159,6 +159,7 @@ async def register_switch(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_switch(var)) + CORE.register_platform_component("switch", var) await setup_switch_core_(var, config) diff --git a/esphome/components/switch/switch.cpp b/esphome/components/switch/switch.cpp index 96611b0b87..c204895755 100644 --- a/esphome/components/switch/switch.cpp +++ b/esphome/components/switch/switch.cpp @@ -65,7 +65,24 @@ bool Switch::is_inverted() const { return this->inverted_; } void log_switch(const char *tag, const char *prefix, const char *type, Switch *obj) { if (obj != nullptr) { - ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str()); + // Prepare restore mode string + const LogString *onoff = LOG_STR(""), *inverted = onoff, *restore; + if (obj->restore_mode & RESTORE_MODE_DISABLED_MASK) { + restore = LOG_STR("disabled"); + } else { + onoff = obj->restore_mode & RESTORE_MODE_ON_MASK ? LOG_STR("ON") : LOG_STR("OFF"); + inverted = obj->restore_mode & RESTORE_MODE_INVERTED_MASK ? LOG_STR("inverted ") : LOG_STR(""); + restore = obj->restore_mode & RESTORE_MODE_PERSISTENT_MASK ? LOG_STR("restore defaults to") : LOG_STR("always"); + } + + // Build the base message with mandatory fields + ESP_LOGCONFIG(tag, + "%s%s '%s'\n" + "%s Restore Mode: %s%s %s", + prefix, type, obj->get_name().c_str(), prefix, LOG_STR_ARG(inverted), LOG_STR_ARG(restore), + LOG_STR_ARG(onoff)); + + // Add optional fields separately if (!obj->get_icon().empty()) { ESP_LOGCONFIG(tag, "%s Icon: '%s'", prefix, obj->get_icon().c_str()); } @@ -78,17 +95,6 @@ void log_switch(const char *tag, const char *prefix, const char *type, Switch *o if (!obj->get_device_class().empty()) { ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); } - const LogString *onoff = LOG_STR(""), *inverted = onoff, *restore; - if (obj->restore_mode & RESTORE_MODE_DISABLED_MASK) { - restore = LOG_STR("disabled"); - } else { - onoff = obj->restore_mode & RESTORE_MODE_ON_MASK ? LOG_STR("ON") : LOG_STR("OFF"); - inverted = obj->restore_mode & RESTORE_MODE_INVERTED_MASK ? LOG_STR("inverted ") : LOG_STR(""); - restore = obj->restore_mode & RESTORE_MODE_PERSISTENT_MASK ? LOG_STR("restore defaults to") : LOG_STR("always"); - } - - ESP_LOGCONFIG(tag, "%s Restore Mode: %s%s %s", prefix, LOG_STR_ARG(inverted), LOG_STR_ARG(restore), - LOG_STR_ARG(onoff)); } } diff --git a/esphome/components/switch/switch.h b/esphome/components/switch/switch.h index b5395a2c83..b999296564 100644 --- a/esphome/components/switch/switch.h +++ b/esphome/components/switch/switch.h @@ -2,8 +2,8 @@ #include "esphome/core/component.h" #include "esphome/core/entity_base.h" -#include "esphome/core/preferences.h" #include "esphome/core/helpers.h" +#include "esphome/core/preferences.h" namespace esphome { namespace switch_ { @@ -21,7 +21,7 @@ const int RESTORE_MODE_PERSISTENT_MASK = 0x02; const int RESTORE_MODE_INVERTED_MASK = 0x04; const int RESTORE_MODE_DISABLED_MASK = 0x08; -enum SwitchRestoreMode { +enum SwitchRestoreMode : uint8_t { SWITCH_ALWAYS_OFF = !RESTORE_MODE_ON_MASK, SWITCH_ALWAYS_ON = RESTORE_MODE_ON_MASK, SWITCH_RESTORE_DEFAULT_OFF = RESTORE_MODE_PERSISTENT_MASK, @@ -49,12 +49,12 @@ class Switch : public EntityBase, public EntityBase_DeviceClass { */ void publish_state(bool state); - /// The current reported state of the binary sensor. - bool state; - /// Indicates whether or not state is to be retrieved from flash and how SwitchRestoreMode restore_mode{SWITCH_RESTORE_DEFAULT_OFF}; + /// The current reported state of the binary sensor. + bool state; + /** Turn this switch on. This is called by the front-end. * * For implementing switches, please override write_state. @@ -123,10 +123,16 @@ class Switch : public EntityBase, public EntityBase_DeviceClass { */ virtual void write_state(bool state) = 0; - CallbackManager state_callback_{}; - bool inverted_{false}; - Deduplicator publish_dedup_; + // Pointer first (4 bytes) ESPPreferenceObject rtc_; + + // CallbackManager (12 bytes on 32-bit - contains vector) + CallbackManager state_callback_{}; + + // Small types grouped together + Deduplicator publish_dedup_; // 2 bytes (bool has_value_ + bool last_value_) + bool inverted_{false}; // 1 byte + // Total: 3 bytes, 1 byte padding }; #define LOG_SWITCH(prefix, type, obj) log_switch((TAG), (prefix), LOG_STR_LITERAL(type), (obj)) diff --git a/esphome/components/sx1509/__init__.py b/esphome/components/sx1509/__init__.py index b1702b5ade..f1b08a505a 100644 --- a/esphome/components/sx1509/__init__.py +++ b/esphome/components/sx1509/__init__.py @@ -1,6 +1,6 @@ -from esphome import pins +from esphome import automation, pins import esphome.codegen as cg -from esphome.components import i2c +from esphome.components import i2c, key_provider import esphome.config_validation as cv from esphome.const import ( CONF_ID, @@ -8,13 +8,16 @@ from esphome.const import ( CONF_INVERTED, CONF_MODE, CONF_NUMBER, + CONF_ON_KEY, CONF_OPEN_DRAIN, CONF_OUTPUT, CONF_PULLDOWN, CONF_PULLUP, + CONF_TRIGGER_ID, ) CONF_KEYPAD = "keypad" +CONF_KEYS = "keys" CONF_KEY_ROWS = "key_rows" CONF_KEY_COLUMNS = "key_columns" CONF_SLEEP_TIME = "sleep_time" @@ -22,22 +25,47 @@ CONF_SCAN_TIME = "scan_time" CONF_DEBOUNCE_TIME = "debounce_time" CONF_SX1509_ID = "sx1509_id" +AUTO_LOAD = ["key_provider"] DEPENDENCIES = ["i2c"] MULTI_CONF = True sx1509_ns = cg.esphome_ns.namespace("sx1509") -SX1509Component = sx1509_ns.class_("SX1509Component", cg.Component, i2c.I2CDevice) +SX1509Component = sx1509_ns.class_( + "SX1509Component", cg.Component, i2c.I2CDevice, key_provider.KeyProvider +) SX1509GPIOPin = sx1509_ns.class_("SX1509GPIOPin", cg.GPIOPin) +SX1509KeyTrigger = sx1509_ns.class_( + "SX1509KeyTrigger", automation.Trigger.template(cg.uint8) +) -KEYPAD_SCHEMA = cv.Schema( - { - cv.Required(CONF_KEY_ROWS): cv.int_range(min=1, max=8), - cv.Required(CONF_KEY_COLUMNS): cv.int_range(min=1, max=8), - cv.Optional(CONF_SLEEP_TIME): cv.int_range(min=128, max=8192), - cv.Optional(CONF_SCAN_TIME): cv.int_range(min=1, max=128), - cv.Optional(CONF_DEBOUNCE_TIME): cv.int_range(min=1, max=64), - } + +def check_keys(config): + if CONF_KEYS in config: + if len(config[CONF_KEYS]) != config[CONF_KEY_ROWS] * config[CONF_KEY_COLUMNS]: + raise cv.Invalid( + "The number of key codes must equal the number of rows * columns" + ) + return config + + +KEYPAD_SCHEMA = cv.All( + cv.Schema( + { + cv.Required(CONF_KEY_ROWS): cv.int_range(min=2, max=8), + cv.Required(CONF_KEY_COLUMNS): cv.int_range(min=1, max=8), + cv.Optional(CONF_SLEEP_TIME): cv.int_range(min=128, max=8192), + cv.Optional(CONF_SCAN_TIME): cv.int_range(min=1, max=128), + cv.Optional(CONF_DEBOUNCE_TIME): cv.int_range(min=1, max=64), + cv.Optional(CONF_KEYS): cv.string, + cv.Optional(CONF_ON_KEY): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SX1509KeyTrigger), + } + ), + } + ), + check_keys, ) CONFIG_SCHEMA = ( @@ -56,17 +84,22 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) await i2c.register_i2c_device(var, config) - if CONF_KEYPAD in config: - keypad = config[CONF_KEYPAD] - cg.add(var.set_rows_cols(keypad[CONF_KEY_ROWS], keypad[CONF_KEY_COLUMNS])) + if conf := config.get(CONF_KEYPAD): + cg.add(var.set_rows_cols(conf[CONF_KEY_ROWS], conf[CONF_KEY_COLUMNS])) if ( - CONF_SLEEP_TIME in keypad - and CONF_SCAN_TIME in keypad - and CONF_DEBOUNCE_TIME in keypad + CONF_SLEEP_TIME in conf + and CONF_SCAN_TIME in conf + and CONF_DEBOUNCE_TIME in conf ): - cg.add(var.set_sleep_time(keypad[CONF_SLEEP_TIME])) - cg.add(var.set_scan_time(keypad[CONF_SCAN_TIME])) - cg.add(var.set_debounce_time(keypad[CONF_DEBOUNCE_TIME])) + cg.add(var.set_sleep_time(conf[CONF_SLEEP_TIME])) + cg.add(var.set_scan_time(conf[CONF_SCAN_TIME])) + cg.add(var.set_debounce_time(conf[CONF_DEBOUNCE_TIME])) + if keys := conf.get(CONF_KEYS): + cg.add(var.set_keys(keys)) + for tconf in conf.get(CONF_ON_KEY, []): + trigger = cg.new_Pvariable(tconf[CONF_TRIGGER_ID]) + cg.add(var.register_key_trigger(trigger)) + await automation.build_automation(trigger, [(cg.uint8, "x")], tconf) def validate_mode(value): diff --git a/esphome/components/sx1509/sx1509.cpp b/esphome/components/sx1509/sx1509.cpp index 855a90bacd..d323c9a92c 100644 --- a/esphome/components/sx1509/sx1509.cpp +++ b/esphome/components/sx1509/sx1509.cpp @@ -8,9 +8,9 @@ namespace sx1509 { static const char *const TAG = "sx1509"; void SX1509Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up SX1509Component..."); + ESP_LOGCONFIG(TAG, "Running setup"); - ESP_LOGV(TAG, " Resetting devices..."); + ESP_LOGV(TAG, " Resetting devices"); if (!this->write_byte(REG_RESET, 0x12)) { this->mark_failed(); return; @@ -48,6 +48,30 @@ void SX1509Component::loop() { uint16_t key_data = this->read_key_data(); for (auto *binary_sensor : this->keypad_binary_sensors_) binary_sensor->process(key_data); + if (this->keys_.empty()) + return; + if (key_data == 0) { + this->last_key_ = 0; + return; + } + int row, col; + for (row = 0; row < 7; row++) { + if (key_data & (1 << row)) + break; + } + for (col = 8; col < 15; col++) { + if (key_data & (1 << col)) + break; + } + col -= 8; + uint8_t key = this->keys_[row * this->cols_ + col]; + if (key == this->last_key_) + return; + this->last_key_ = key; + ESP_LOGV(TAG, "row %d, col %d, key '%c'", row, col, key); + for (auto &trigger : this->key_triggers_) + trigger->trigger(key); + this->send_key_(key); } } @@ -230,9 +254,9 @@ void SX1509Component::setup_keypad_() { scan_time_bits &= 0b111; // Scan time is bits 2:0 temp_byte = sleep_time_ | scan_time_bits; this->write_byte(REG_KEY_CONFIG_1, temp_byte); - rows_ = (rows_ - 1) & 0b111; // 0 = off, 0b001 = 2 rows, 0b111 = 8 rows, etc. - cols_ = (cols_ - 1) & 0b111; // 0b000 = 1 column, ob111 = 8 columns, etc. - this->write_byte(REG_KEY_CONFIG_2, (rows_ << 3) | cols_); + temp_byte = ((this->rows_ - 1) & 0b111) << 3; // 0 = off, 0b001 = 2 rows, 0b111 = 8 rows, etc. + temp_byte |= (this->cols_ - 1) & 0b111; // 0b000 = 1 column, ob111 = 8 columns, etc. + this->write_byte(REG_KEY_CONFIG_2, temp_byte); } uint16_t SX1509Component::read_key_data() { diff --git a/esphome/components/sx1509/sx1509.h b/esphome/components/sx1509/sx1509.h index 9e4f31aab0..c0e86aa8a1 100644 --- a/esphome/components/sx1509/sx1509.h +++ b/esphome/components/sx1509/sx1509.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/components/i2c/i2c.h" +#include "esphome/components/key_provider/key_provider.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" #include "sx1509_gpio_pin.h" @@ -27,7 +28,9 @@ class SX1509Processor { virtual void process(uint16_t data){}; }; -class SX1509Component : public Component, public i2c::I2CDevice { +class SX1509KeyTrigger : public Trigger {}; + +class SX1509Component : public Component, public i2c::I2CDevice, public key_provider::KeyProvider { public: SX1509Component() = default; @@ -47,12 +50,14 @@ class SX1509Component : public Component, public i2c::I2CDevice { this->cols_ = cols; this->has_keypad_ = true; }; + void set_keys(std::string keys) { this->keys_ = std::move(keys); }; void set_sleep_time(uint16_t sleep_time) { this->sleep_time_ = sleep_time; }; void set_scan_time(uint8_t scan_time) { this->scan_time_ = scan_time; }; void set_debounce_time(uint8_t debounce_time = 1) { this->debounce_time_ = debounce_time; }; void register_keypad_binary_sensor(SX1509Processor *binary_sensor) { this->keypad_binary_sensors_.push_back(binary_sensor); } + void register_key_trigger(SX1509KeyTrigger *trig) { this->key_triggers_.push_back(trig); }; void setup_led_driver(uint8_t pin); protected: @@ -65,10 +70,13 @@ class SX1509Component : public Component, public i2c::I2CDevice { bool has_keypad_ = false; uint8_t rows_ = 0; uint8_t cols_ = 0; + std::string keys_; uint16_t sleep_time_ = 128; uint8_t scan_time_ = 1; uint8_t debounce_time_ = 1; + uint8_t last_key_ = 0; std::vector keypad_binary_sensors_; + std::vector key_triggers_; uint32_t last_loop_timestamp_ = 0; const uint32_t min_loop_period_ = 15; // ms diff --git a/esphome/components/t6615/t6615.cpp b/esphome/components/t6615/t6615.cpp index 1c78833500..c2ac88ee2e 100644 --- a/esphome/components/t6615/t6615.cpp +++ b/esphome/components/t6615/t6615.cpp @@ -1,4 +1,5 @@ #include "t6615.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/tc74/tc74.cpp b/esphome/components/tc74/tc74.cpp index 2acb71f921..b79bcb5592 100644 --- a/esphome/components/tc74/tc74.cpp +++ b/esphome/components/tc74/tc74.cpp @@ -15,7 +15,7 @@ static const uint8_t TC74_DATA_READY_MASK = 0x40; // It is possible the "Data Ready" bit will not be set if the TC74 has not been powered on for at least 250ms, so it not // being set does not constitute a failure. void TC74Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up TC74..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t config_reg; if (this->read_register(TC74_REGISTER_CONFIGURATION, &config_reg, 1) != i2c::ERROR_OK) { this->mark_failed(); diff --git a/esphome/components/tca9548a/tca9548a.cpp b/esphome/components/tca9548a/tca9548a.cpp index 770fd5e47c..cdeb94ceca 100644 --- a/esphome/components/tca9548a/tca9548a.cpp +++ b/esphome/components/tca9548a/tca9548a.cpp @@ -24,7 +24,7 @@ i2c::ErrorCode TCA9548AChannel::writev(uint8_t address, i2c::WriteBuffer *buffer } void TCA9548AComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up TCA9548A..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t status = 0; if (this->read(&status, 1) != i2c::ERROR_OK) { ESP_LOGE(TAG, "TCA9548A failed"); diff --git a/esphome/components/tca9555/tca9555.cpp b/esphome/components/tca9555/tca9555.cpp index e065398c46..7bd2f44918 100644 --- a/esphome/components/tca9555/tca9555.cpp +++ b/esphome/components/tca9555/tca9555.cpp @@ -16,7 +16,7 @@ namespace tca9555 { static const char *const TAG = "tca9555"; void TCA9555Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up TCA9555..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->read_gpio_modes_()) { this->mark_failed(); return; @@ -30,7 +30,7 @@ void TCA9555Component::dump_config() { ESP_LOGCONFIG(TAG, "TCA9555:"); LOG_I2C_DEVICE(this) if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with TCA9555 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } void TCA9555Component::pin_mode(uint8_t pin, gpio::Flags flags) { diff --git a/esphome/components/tcs34725/tcs34725.cpp b/esphome/components/tcs34725/tcs34725.cpp index 0830004b5a..9926ebc553 100644 --- a/esphome/components/tcs34725/tcs34725.cpp +++ b/esphome/components/tcs34725/tcs34725.cpp @@ -1,8 +1,8 @@ #include "tcs34725.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" -#include #include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include namespace esphome { namespace tcs34725 { @@ -18,7 +18,7 @@ static const uint8_t TCS34725_REGISTER_ENABLE = TCS34725_COMMAND_BIT | 0x00; static const uint8_t TCS34725_REGISTER_CRGBDATAL = TCS34725_COMMAND_BIT | 0x14; void TCS34725Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up TCS34725..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t id; if (this->read_register(TCS34725_REGISTER_ID, &id, 1) != i2c::ERROR_OK) { this->mark_failed(); @@ -46,7 +46,7 @@ void TCS34725Component::dump_config() { ESP_LOGCONFIG(TAG, "TCS34725:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with TCS34725 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/tee501/tee501.cpp b/esphome/components/tee501/tee501.cpp index 22329d40cd..45241627f9 100644 --- a/esphome/components/tee501/tee501.cpp +++ b/esphome/components/tee501/tee501.cpp @@ -1,4 +1,5 @@ #include "tee501.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -7,7 +8,7 @@ namespace tee501 { static const char *const TAG = "tee501"; void TEE501Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up TEE501..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t address[] = {0x70, 0x29}; this->write(address, 2, false); uint8_t identification[9]; @@ -25,7 +26,7 @@ void TEE501Component::dump_config() { LOG_I2C_DEVICE(this); switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Communication with TEE501 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case CRC_CHECK_FAILED: ESP_LOGE(TAG, "The crc check failed"); diff --git a/esphome/components/tem3200/tem3200.cpp b/esphome/components/tem3200/tem3200.cpp index d8ce48af90..c0655d02b8 100644 --- a/esphome/components/tem3200/tem3200.cpp +++ b/esphome/components/tem3200/tem3200.cpp @@ -1,7 +1,7 @@ #include "tem3200.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace tem3200 { @@ -16,7 +16,7 @@ enum ErrorCode { }; void TEM3200Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up TEM3200..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t status(NONE); uint16_t raw_temperature(0); @@ -24,7 +24,7 @@ void TEM3200Component::setup() { i2c::ErrorCode err = this->read_(status, raw_temperature, raw_pressure); if (err != i2c::ERROR_OK) { - ESP_LOGCONFIG(TAG, " I2C Communication Failed..."); + ESP_LOGCONFIG(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); return; } @@ -43,7 +43,6 @@ void TEM3200Component::setup() { this->status_set_warning(); break; } - ESP_LOGCONFIG(TAG, " Success..."); } void TEM3200Component::dump_config() { @@ -115,7 +114,7 @@ void TEM3200Component::update() { i2c::ErrorCode err = this->read_(status, raw_temperature, raw_pressure); if (err != i2c::ERROR_OK) { - ESP_LOGW(TAG, "I2C Communication Failed"); + ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning(); return; } diff --git a/esphome/components/template/alarm_control_panel/__init__.py b/esphome/components/template/alarm_control_panel/__init__.py index a406c626ee..5d2421fcbc 100644 --- a/esphome/components/template/alarm_control_panel/__init__.py +++ b/esphome/components/template/alarm_control_panel/__init__.py @@ -10,6 +10,7 @@ CODEOWNERS = ["@grahambrown11", "@hwstar"] CONF_CODES = "codes" CONF_BYPASS_ARMED_HOME = "bypass_armed_home" CONF_BYPASS_ARMED_NIGHT = "bypass_armed_night" +CONF_BYPASS_AUTO = "bypass_auto" CONF_CHIME = "chime" CONF_TRIGGER_MODE = "trigger_mode" CONF_REQUIRES_CODE_TO_ARM = "requires_code_to_arm" @@ -23,6 +24,7 @@ CONF_TRIGGER_TIME = "trigger_time" FLAG_NORMAL = "normal" FLAG_BYPASS_ARMED_HOME = "bypass_armed_home" FLAG_BYPASS_ARMED_NIGHT = "bypass_armed_night" +FLAG_BYPASS_AUTO = "bypass_auto" FLAG_CHIME = "chime" BinarySensorFlags = { @@ -30,6 +32,7 @@ BinarySensorFlags = { FLAG_BYPASS_ARMED_HOME: 1 << 1, FLAG_BYPASS_ARMED_NIGHT: 1 << 2, FLAG_CHIME: 1 << 3, + FLAG_BYPASS_AUTO: 1 << 4, } @@ -68,6 +71,7 @@ TEMPLATE_ALARM_CONTROL_PANEL_BINARY_SENSOR_SCHEMA = cv.maybe_simple_value( cv.Required(CONF_INPUT): cv.use_id(binary_sensor.BinarySensor), cv.Optional(CONF_BYPASS_ARMED_HOME, default=False): cv.boolean, cv.Optional(CONF_BYPASS_ARMED_NIGHT, default=False): cv.boolean, + cv.Optional(CONF_BYPASS_AUTO, default=False): cv.boolean, cv.Optional(CONF_CHIME, default=False): cv.boolean, cv.Optional(CONF_TRIGGER_MODE, default="DELAYED"): cv.enum( ALARM_SENSOR_TYPES, upper=True, space="_" @@ -143,6 +147,8 @@ async def to_code(config): if sensor[CONF_BYPASS_ARMED_NIGHT]: flags |= BinarySensorFlags[FLAG_BYPASS_ARMED_NIGHT] supports_arm_night = True + if sensor[CONF_BYPASS_AUTO]: + flags |= BinarySensorFlags[FLAG_BYPASS_AUTO] if sensor[CONF_CHIME]: flags |= BinarySensorFlags[FLAG_CHIME] cg.add(var.add_sensor(bs, flags, sensor[CONF_TRIGGER_MODE])) diff --git a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp index bf1338edbe..6f743a77ef 100644 --- a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +++ b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp @@ -29,8 +29,10 @@ void TemplateAlarmControlPanel::add_sensor(binary_sensor::BinarySensor *sensor, void TemplateAlarmControlPanel::dump_config() { ESP_LOGCONFIG(TAG, "TemplateAlarmControlPanel:"); - ESP_LOGCONFIG(TAG, " Current State: %s", LOG_STR_ARG(alarm_control_panel_state_to_string(this->current_state_))); - ESP_LOGCONFIG(TAG, " Number of Codes: %u", this->codes_.size()); + ESP_LOGCONFIG(TAG, + " Current State: %s\n" + " Number of Codes: %u", + LOG_STR_ARG(alarm_control_panel_state_to_string(this->current_state_)), this->codes_.size()); if (!this->codes_.empty()) ESP_LOGCONFIG(TAG, " Requires Code To Arm: %s", YESNO(this->requires_code_to_arm_)); ESP_LOGCONFIG(TAG, " Arming Away Time: %" PRIu32 "s", (this->arming_away_time_ / 1000)); @@ -38,18 +40,25 @@ void TemplateAlarmControlPanel::dump_config() { ESP_LOGCONFIG(TAG, " Arming Home Time: %" PRIu32 "s", (this->arming_home_time_ / 1000)); if (this->arming_night_time_ != 0) ESP_LOGCONFIG(TAG, " Arming Night Time: %" PRIu32 "s", (this->arming_night_time_ / 1000)); - ESP_LOGCONFIG(TAG, " Pending Time: %" PRIu32 "s", (this->pending_time_ / 1000)); - ESP_LOGCONFIG(TAG, " Trigger Time: %" PRIu32 "s", (this->trigger_time_ / 1000)); - ESP_LOGCONFIG(TAG, " Supported Features: %" PRIu32, this->get_supported_features()); + ESP_LOGCONFIG(TAG, + " Pending Time: %" PRIu32 "s\n" + " Trigger Time: %" PRIu32 "s\n" + " Supported Features: %" PRIu32, + (this->pending_time_ / 1000), (this->trigger_time_ / 1000), this->get_supported_features()); #ifdef USE_BINARY_SENSOR for (auto sensor_info : this->sensor_map_) { ESP_LOGCONFIG(TAG, " Binary Sensor:"); - ESP_LOGCONFIG(TAG, " Name: %s", sensor_info.first->get_name().c_str()); - ESP_LOGCONFIG(TAG, " Armed home bypass: %s", - TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME)); - ESP_LOGCONFIG(TAG, " Armed night bypass: %s", - TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT)); - ESP_LOGCONFIG(TAG, " Chime mode: %s", TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_CHIME)); + ESP_LOGCONFIG(TAG, + " Name: %s\n" + " Armed home bypass: %s\n" + " Armed night bypass: %s\n" + " Auto bypass: %s\n" + " Chime mode: %s", + sensor_info.first->get_name().c_str(), + TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME), + TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT), + TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO), + TRUEFALSE(sensor_info.second.flags & BINARY_SENSOR_MODE_CHIME)); const char *sensor_type; switch (sensor_info.second.type) { case ALARM_SENSOR_TYPE_INSTANT: @@ -71,7 +80,7 @@ void TemplateAlarmControlPanel::dump_config() { } void TemplateAlarmControlPanel::setup() { - ESP_LOGCONFIG(TAG, "Setting up Template AlarmControlPanel '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); switch (this->restore_mode_) { case ALARM_CONTROL_PANEL_ALWAYS_DISARMED: this->current_state_ = ACP_STATE_DISARMED; @@ -101,6 +110,7 @@ void TemplateAlarmControlPanel::loop() { delay = this->arming_night_time_; } if ((millis() - this->last_update_) > delay) { + this->bypass_before_arming(); this->publish_state(this->desired_state_); } return; @@ -137,6 +147,11 @@ void TemplateAlarmControlPanel::loop() { } // Check for triggered sensors if (sensor_info.first->state) { // Sensor triggered? + // Skip if auto bypassed + if (std::count(this->bypassed_sensor_indicies_.begin(), this->bypassed_sensor_indicies_.end(), + sensor_info.second.store_index) == 1) { + continue; + } // Skip if bypass armed home if (this->current_state_ == ACP_STATE_ARMED_HOME && (sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME)) { @@ -236,10 +251,23 @@ void TemplateAlarmControlPanel::arm_(optional code, alarm_control_p if (delay > 0) { this->publish_state(ACP_STATE_ARMING); } else { + this->bypass_before_arming(); this->publish_state(state); } } +void TemplateAlarmControlPanel::bypass_before_arming() { +#ifdef USE_BINARY_SENSOR + for (auto sensor_info : this->sensor_map_) { + // Check for sensors left on and set to bypass automatically and remove them from monitoring + if ((sensor_info.second.flags & BINARY_SENSOR_MODE_BYPASS_AUTO) && (sensor_info.first->state)) { + ESP_LOGW(TAG, "'%s' is left on and will be automatically bypassed", sensor_info.first->get_name().c_str()); + this->bypassed_sensor_indicies_.push_back(sensor_info.second.store_index); + } + } +#endif +} + void TemplateAlarmControlPanel::control(const AlarmControlPanelCall &call) { if (call.get_state()) { if (call.get_state() == ACP_STATE_ARMED_AWAY) { @@ -255,6 +283,9 @@ void TemplateAlarmControlPanel::control(const AlarmControlPanelCall &call) { } this->desired_state_ = ACP_STATE_DISARMED; this->publish_state(ACP_STATE_DISARMED); +#ifdef USE_BINARY_SENSOR + this->bypassed_sensor_indicies_.clear(); +#endif } else if (call.get_state() == ACP_STATE_TRIGGERED) { this->publish_state(ACP_STATE_TRIGGERED); } else if (call.get_state() == ACP_STATE_PENDING) { diff --git a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h index b29a48dfd7..c3b28e8efa 100644 --- a/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +++ b/esphome/components/template/alarm_control_panel/template_alarm_control_panel.h @@ -22,6 +22,7 @@ enum BinarySensorFlags : uint16_t { BINARY_SENSOR_MODE_BYPASS_ARMED_HOME = 1 << 1, BINARY_SENSOR_MODE_BYPASS_ARMED_NIGHT = 1 << 2, BINARY_SENSOR_MODE_CHIME = 1 << 3, + BINARY_SENSOR_MODE_BYPASS_AUTO = 1 << 4, }; enum AlarmSensorType : uint16_t { @@ -59,6 +60,7 @@ class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, bool get_requires_code_to_arm() const override { return this->requires_code_to_arm_; } bool get_all_sensors_ready() { return this->sensors_ready_; }; void set_restore_mode(TemplateAlarmControlPanelRestoreMode restore_mode) { this->restore_mode_ = restore_mode; } + void bypass_before_arming(); #ifdef USE_BINARY_SENSOR /** Add a binary_sensor to the alarm_panel. @@ -121,7 +123,8 @@ class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, #ifdef USE_BINARY_SENSOR // This maps a binary sensor to its type and attribute bits std::map sensor_map_; - + // a list of automatically bypassed sensors + std::vector bypassed_sensor_indicies_; #endif TemplateAlarmControlPanelRestoreMode restore_mode_{}; diff --git a/esphome/components/template/cover/template_cover.cpp b/esphome/components/template/cover/template_cover.cpp index 2d6c3087ae..d32c6ac546 100644 --- a/esphome/components/template/cover/template_cover.cpp +++ b/esphome/components/template/cover/template_cover.cpp @@ -16,7 +16,7 @@ TemplateCover::TemplateCover() position_trigger_(new Trigger()), tilt_trigger_(new Trigger()) {} void TemplateCover::setup() { - ESP_LOGCONFIG(TAG, "Setting up template cover '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); switch (this->restore_mode_) { case COVER_NO_RESTORE: break; diff --git a/esphome/components/template/select/template_select.cpp b/esphome/components/template/select/template_select.cpp index 88a0d66ed6..0160fab04b 100644 --- a/esphome/components/template/select/template_select.cpp +++ b/esphome/components/template/select/template_select.cpp @@ -11,7 +11,7 @@ void TemplateSelect::setup() { return; std::string value; - ESP_LOGD(TAG, "Setting up Template Select"); + ESP_LOGD(TAG, "Setting up"); if (!this->restore_value_) { value = this->initial_option_; ESP_LOGD(TAG, "State from initial: %s", value.c_str()); @@ -66,9 +66,11 @@ void TemplateSelect::dump_config() { LOG_UPDATE_INTERVAL(this); if (this->f_.has_value()) return; - ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_)); - ESP_LOGCONFIG(TAG, " Initial Option: %s", this->initial_option_.c_str()); - ESP_LOGCONFIG(TAG, " Restore Value: %s", YESNO(this->restore_value_)); + ESP_LOGCONFIG(TAG, + " Optimistic: %s\n" + " Initial Option: %s\n" + " Restore Value: %s", + YESNO(this->optimistic_), this->initial_option_.c_str(), YESNO(this->restore_value_)); } } // namespace template_ diff --git a/esphome/components/template/text/template_text.cpp b/esphome/components/template/text/template_text.cpp index fb0208e198..f8d883e848 100644 --- a/esphome/components/template/text/template_text.cpp +++ b/esphome/components/template/text/template_text.cpp @@ -12,9 +12,8 @@ void TemplateText::setup() { return; } - std::string value; - ESP_LOGD(TAG, "Setting up Template Text Input"); - value = this->initial_value_; + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); + std::string value = this->initial_value_; if (!this->pref_) { ESP_LOGD(TAG, "State from initial: %s", value.c_str()); } else { diff --git a/esphome/components/template/valve/template_valve.cpp b/esphome/components/template/valve/template_valve.cpp index f943e19da9..8421f5e06f 100644 --- a/esphome/components/template/valve/template_valve.cpp +++ b/esphome/components/template/valve/template_valve.cpp @@ -16,7 +16,7 @@ TemplateValve::TemplateValve() position_trigger_(new Trigger()) {} void TemplateValve::setup() { - ESP_LOGCONFIG(TAG, "Setting up template valve '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); switch (this->restore_mode_) { case VALVE_NO_RESTORE: break; @@ -66,8 +66,10 @@ Trigger<> *TemplateValve::get_toggle_trigger() const { return this->toggle_trigg void TemplateValve::dump_config() { LOG_VALVE("", "Template Valve", this); - ESP_LOGCONFIG(TAG, " Has position: %s", YESNO(this->has_position_)); - ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_)); + ESP_LOGCONFIG(TAG, + " Has position: %s\n" + " Optimistic: %s", + YESNO(this->has_position_), YESNO(this->optimistic_)); } void TemplateValve::control(const ValveCall &call) { diff --git a/esphome/components/text/__init__.py b/esphome/components/text/__init__.py index 1cc9283e45..40b3a90d6b 100644 --- a/esphome/components/text/__init__.py +++ b/esphome/components/text/__init__.py @@ -1,5 +1,3 @@ -from typing import Optional - from esphome import automation import esphome.codegen as cg from esphome.components import mqtt, web_server @@ -92,9 +90,9 @@ async def setup_text_core_( var, config, *, - min_length: Optional[int], - max_length: Optional[int], - pattern: Optional[str], + min_length: int | None, + max_length: int | None, + pattern: str | None, ): await setup_entity(var, config) @@ -121,13 +119,14 @@ async def register_text( var, config, *, - min_length: Optional[int] = 0, - max_length: Optional[int] = 255, - pattern: Optional[str] = None, + min_length: int | None = 0, + max_length: int | None = 255, + pattern: str | None = None, ): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_text(var)) + CORE.register_platform_component("text", var) await setup_text_core_( var, config, min_length=min_length, max_length=max_length, pattern=pattern ) @@ -136,9 +135,9 @@ async def register_text( async def new_text( config, *, - min_length: Optional[int] = 0, - max_length: Optional[int] = 255, - pattern: Optional[str] = None, + min_length: int | None = 0, + max_length: int | None = 255, + pattern: str | None = None, ): var = cg.new_Pvariable(config[CONF_ID]) await register_text( diff --git a/esphome/components/text/text.cpp b/esphome/components/text/text.cpp index 8f0242e747..654893d4e4 100644 --- a/esphome/components/text/text.cpp +++ b/esphome/components/text/text.cpp @@ -7,7 +7,7 @@ namespace text { static const char *const TAG = "text"; void Text::publish_state(const std::string &state) { - this->has_state_ = true; + this->set_has_state(true); this->state = state; if (this->traits.get_mode() == TEXT_MODE_PASSWORD) { ESP_LOGD(TAG, "'%s': Sending state " LOG_SECRET("'%s'"), this->get_name().c_str(), state.c_str()); diff --git a/esphome/components/text/text.h b/esphome/components/text/text.h index f71dde69ba..3cc0cefc3e 100644 --- a/esphome/components/text/text.h +++ b/esphome/components/text/text.h @@ -28,9 +28,6 @@ class Text : public EntityBase { void publish_state(const std::string &state); - /// Return whether this text input has gotten a full state yet. - bool has_state() const { return has_state_; } - /// Instantiate a TextCall object to modify this text component's state. TextCall make_call() { return TextCall(this); } @@ -48,7 +45,6 @@ class Text : public EntityBase { virtual void control(const std::string &value) = 0; CallbackManager state_callback_; - bool has_state_{false}; }; } // namespace text diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index 888b65745f..c7ac17c35a 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -215,6 +215,7 @@ async def register_text_sensor(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_text_sensor(var)) + CORE.register_platform_component("text_sensor", var) await setup_text_sensor_core_(var, config) diff --git a/esphome/components/text_sensor/text_sensor.cpp b/esphome/components/text_sensor/text_sensor.cpp index f10cd50267..c57e0ffefb 100644 --- a/esphome/components/text_sensor/text_sensor.cpp +++ b/esphome/components/text_sensor/text_sensor.cpp @@ -8,7 +8,9 @@ static const char *const TAG = "text_sensor"; void TextSensor::publish_state(const std::string &state) { this->raw_state = state; - this->raw_callback_.call(state); + if (this->raw_callback_) { + this->raw_callback_->call(state); + } ESP_LOGV(TAG, "'%s': Received new state %s", this->name_.c_str(), state.c_str()); @@ -53,20 +55,22 @@ void TextSensor::add_on_state_callback(std::function callback this->callback_.add(std::move(callback)); } void TextSensor::add_on_raw_state_callback(std::function callback) { - this->raw_callback_.add(std::move(callback)); + if (!this->raw_callback_) { + this->raw_callback_ = make_unique>(); + } + this->raw_callback_->add(std::move(callback)); } std::string TextSensor::get_state() const { return this->state; } std::string TextSensor::get_raw_state() const { return this->raw_state; } void TextSensor::internal_send_state_to_frontend(const std::string &state) { this->state = state; - this->has_state_ = true; + this->set_has_state(true); ESP_LOGD(TAG, "'%s': Sending state '%s'", this->name_.c_str(), state.c_str()); this->callback_.call(state); } std::string TextSensor::unique_id() { return ""; } -bool TextSensor::has_state() { return this->has_state_; } } // namespace text_sensor } // namespace esphome diff --git a/esphome/components/text_sensor/text_sensor.h b/esphome/components/text_sensor/text_sensor.h index bd72ea70e3..b27145aa18 100644 --- a/esphome/components/text_sensor/text_sensor.h +++ b/esphome/components/text_sensor/text_sensor.h @@ -6,6 +6,7 @@ #include "esphome/components/text_sensor/filter.h" #include +#include namespace esphome { namespace text_sensor { @@ -33,6 +34,8 @@ namespace text_sensor { class TextSensor : public EntityBase, public EntityBase_DeviceClass { public: + TextSensor() = default; + /// Getter-syntax for .state. std::string get_state() const; /// Getter-syntax for .raw_state @@ -67,17 +70,14 @@ class TextSensor : public EntityBase, public EntityBase_DeviceClass { */ virtual std::string unique_id(); - bool has_state(); - void internal_send_state_to_frontend(const std::string &state); protected: - CallbackManager raw_callback_; ///< Storage for raw state callbacks. - CallbackManager callback_; ///< Storage for filtered state callbacks. + std::unique_ptr> + raw_callback_; ///< Storage for raw state callbacks (lazy allocated). + CallbackManager callback_; ///< Storage for filtered state callbacks. Filter *filter_list_{nullptr}; ///< Store all active filters. - - bool has_state_{false}; }; } // namespace text_sensor diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index f7b3410df9..fe6ed8b159 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -537,7 +537,7 @@ void ThermostatClimate::switch_to_supplemental_action_(climate::ClimateAction ac default: return; } - ESP_LOGVV(TAG, "Updating supplemental action..."); + ESP_LOGVV(TAG, "Updating supplemental action"); this->supplemental_action_ = action; this->trigger_supplemental_action_(); } @@ -1300,37 +1300,48 @@ void ThermostatClimate::dump_config() { } ESP_LOGCONFIG(TAG, " Start-up Delay Enabled: %s", YESNO(this->use_startup_delay_)); if (this->supports_cool_) { - ESP_LOGCONFIG(TAG, " Cooling Parameters:"); - ESP_LOGCONFIG(TAG, " Deadband: %.1f°C", this->cooling_deadband_); - ESP_LOGCONFIG(TAG, " Overrun: %.1f°C", this->cooling_overrun_); + ESP_LOGCONFIG(TAG, + " Cooling Parameters:\n" + " Deadband: %.1f°C\n" + " Overrun: %.1f°C", + this->cooling_deadband_, this->cooling_overrun_); if ((this->supplemental_cool_delta_ > 0) || (this->timer_duration_(thermostat::TIMER_COOLING_MAX_RUN_TIME) > 0)) { - ESP_LOGCONFIG(TAG, " Supplemental Delta: %.1f°C", this->supplemental_cool_delta_); - ESP_LOGCONFIG(TAG, " Maximum Run Time: %" PRIu32 "s", + ESP_LOGCONFIG(TAG, + " Supplemental Delta: %.1f°C\n" + " Maximum Run Time: %" PRIu32 "s", + this->supplemental_cool_delta_, this->timer_duration_(thermostat::TIMER_COOLING_MAX_RUN_TIME) / 1000); } - ESP_LOGCONFIG(TAG, " Minimum Off Time: %" PRIu32 "s", - this->timer_duration_(thermostat::TIMER_COOLING_OFF) / 1000); - ESP_LOGCONFIG(TAG, " Minimum Run Time: %" PRIu32 "s", + ESP_LOGCONFIG(TAG, + " Minimum Off Time: %" PRIu32 "s\n" + " Minimum Run Time: %" PRIu32 "s", + this->timer_duration_(thermostat::TIMER_COOLING_OFF) / 1000, this->timer_duration_(thermostat::TIMER_COOLING_ON) / 1000); } if (this->supports_heat_) { - ESP_LOGCONFIG(TAG, " Heating Parameters:"); - ESP_LOGCONFIG(TAG, " Deadband: %.1f°C", this->heating_deadband_); - ESP_LOGCONFIG(TAG, " Overrun: %.1f°C", this->heating_overrun_); + ESP_LOGCONFIG(TAG, + " Heating Parameters:\n" + " Deadband: %.1f°C\n" + " Overrun: %.1f°C", + this->heating_deadband_, this->heating_overrun_); if ((this->supplemental_heat_delta_ > 0) || (this->timer_duration_(thermostat::TIMER_HEATING_MAX_RUN_TIME) > 0)) { - ESP_LOGCONFIG(TAG, " Supplemental Delta: %.1f°C", this->supplemental_heat_delta_); - ESP_LOGCONFIG(TAG, " Maximum Run Time: %" PRIu32 "s", + ESP_LOGCONFIG(TAG, + " Supplemental Delta: %.1f°C\n" + " Maximum Run Time: %" PRIu32 "s", + this->supplemental_heat_delta_, this->timer_duration_(thermostat::TIMER_HEATING_MAX_RUN_TIME) / 1000); } - ESP_LOGCONFIG(TAG, " Minimum Off Time: %" PRIu32 "s", - this->timer_duration_(thermostat::TIMER_HEATING_OFF) / 1000); - ESP_LOGCONFIG(TAG, " Minimum Run Time: %" PRIu32 "s", + ESP_LOGCONFIG(TAG, + " Minimum Off Time: %" PRIu32 "s\n" + " Minimum Run Time: %" PRIu32 "s", + this->timer_duration_(thermostat::TIMER_HEATING_OFF) / 1000, this->timer_duration_(thermostat::TIMER_HEATING_ON) / 1000); } if (this->supports_fan_only_) { - ESP_LOGCONFIG(TAG, " Fanning Minimum Off Time: %" PRIu32 "s", - this->timer_duration_(thermostat::TIMER_FANNING_OFF) / 1000); - ESP_LOGCONFIG(TAG, " Fanning Minimum Run Time: %" PRIu32 "s", + ESP_LOGCONFIG(TAG, + " Fanning Minimum Off Time: %" PRIu32 "s\n" + " Fanning Minimum Run Time: %" PRIu32 "s", + this->timer_duration_(thermostat::TIMER_FANNING_OFF) / 1000, this->timer_duration_(thermostat::TIMER_FANNING_ON) / 1000); } if (this->supports_fan_mode_on_ || this->supports_fan_mode_off_ || this->supports_fan_mode_auto_ || @@ -1341,14 +1352,17 @@ void ThermostatClimate::dump_config() { this->timer_duration_(thermostat::TIMER_FAN_MODE) / 1000); } ESP_LOGCONFIG(TAG, " Minimum Idle Time: %" PRIu32 "s", this->timer_[thermostat::TIMER_IDLE_ON].time / 1000); - ESP_LOGCONFIG(TAG, " Supports AUTO: %s", YESNO(this->supports_auto_)); - ESP_LOGCONFIG(TAG, " Supports HEAT/COOL: %s", YESNO(this->supports_heat_cool_)); - ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_)); - ESP_LOGCONFIG(TAG, " Supports DRY: %s", YESNO(this->supports_dry_)); - ESP_LOGCONFIG(TAG, " Supports FAN_ONLY: %s", YESNO(this->supports_fan_only_)); - ESP_LOGCONFIG(TAG, " Supports FAN_ONLY_ACTION_USES_FAN_MODE_TIMER: %s", - YESNO(this->supports_fan_only_action_uses_fan_mode_timer_)); - ESP_LOGCONFIG(TAG, " Supports FAN_ONLY_COOLING: %s", YESNO(this->supports_fan_only_cooling_)); + ESP_LOGCONFIG(TAG, + " Supports AUTO: %s\n" + " Supports HEAT/COOL: %s\n" + " Supports COOL: %s\n" + " Supports DRY: %s\n" + " Supports FAN_ONLY: %s\n" + " Supports FAN_ONLY_ACTION_USES_FAN_MODE_TIMER: %s\n" + " Supports FAN_ONLY_COOLING: %s", + YESNO(this->supports_auto_), YESNO(this->supports_heat_cool_), YESNO(this->supports_cool_), + YESNO(this->supports_dry_), YESNO(this->supports_fan_only_), + YESNO(this->supports_fan_only_action_uses_fan_mode_timer_), YESNO(this->supports_fan_only_cooling_)); if (this->supports_cool_) { ESP_LOGCONFIG(TAG, " Supports FAN_WITH_COOLING: %s", YESNO(this->supports_fan_with_cooling_)); } @@ -1356,21 +1370,31 @@ void ThermostatClimate::dump_config() { ESP_LOGCONFIG(TAG, " Supports FAN_WITH_HEATING: %s", YESNO(this->supports_fan_with_heating_)); } ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE ON: %s", YESNO(this->supports_fan_mode_on_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE OFF: %s", YESNO(this->supports_fan_mode_off_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE AUTO: %s", YESNO(this->supports_fan_mode_auto_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE LOW: %s", YESNO(this->supports_fan_mode_low_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE MEDIUM: %s", YESNO(this->supports_fan_mode_medium_)); - ESP_LOGCONFIG(TAG, " Supports FAN MODE HIGH: %s", YESNO(this->supports_fan_mode_high_)); - 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_)); - ESP_LOGCONFIG(TAG, " Supports SWING MODE VERTICAL: %s", YESNO(this->supports_swing_mode_vertical_)); - ESP_LOGCONFIG(TAG, " Supports TWO SET POINTS: %s", YESNO(this->supports_two_points_)); + ESP_LOGCONFIG(TAG, + " Supports FAN MODE ON: %s\n" + " Supports FAN MODE OFF: %s\n" + " Supports FAN MODE AUTO: %s\n" + " Supports FAN MODE LOW: %s\n" + " Supports FAN MODE MEDIUM: %s\n" + " Supports FAN MODE HIGH: %s\n" + " Supports FAN MODE MIDDLE: %s\n" + " Supports FAN MODE FOCUS: %s\n" + " Supports FAN MODE DIFFUSE: %s\n" + " Supports FAN MODE QUIET: %s", + YESNO(this->supports_fan_mode_on_), YESNO(this->supports_fan_mode_off_), + YESNO(this->supports_fan_mode_auto_), YESNO(this->supports_fan_mode_low_), + YESNO(this->supports_fan_mode_medium_), YESNO(this->supports_fan_mode_high_), + YESNO(this->supports_fan_mode_middle_), YESNO(this->supports_fan_mode_focus_), + YESNO(this->supports_fan_mode_diffuse_), YESNO(this->supports_fan_mode_quiet_)); + ESP_LOGCONFIG(TAG, + " Supports SWING MODE BOTH: %s\n" + " Supports SWING MODE OFF: %s\n" + " Supports SWING MODE HORIZONTAL: %s\n" + " Supports SWING MODE VERTICAL: %s\n" + " Supports TWO SET POINTS: %s", + YESNO(this->supports_swing_mode_both_), YESNO(this->supports_swing_mode_off_), + YESNO(this->supports_swing_mode_horizontal_), YESNO(this->supports_swing_mode_vertical_), + YESNO(this->supports_two_points_)); ESP_LOGCONFIG(TAG, " Supported PRESETS: "); for (auto &it : this->preset_config_) { diff --git a/esphome/components/time/__init__.py b/esphome/components/time/__init__.py index 6a3368ca73..6b3ff6f4d3 100644 --- a/esphome/components/time/__init__.py +++ b/esphome/components/time/__init__.py @@ -1,6 +1,5 @@ from importlib import resources import logging -from typing import Optional import tzlocal @@ -40,7 +39,7 @@ SyncTrigger = time_ns.class_("SyncTrigger", automation.Trigger.template(), cg.Co TimeHasTimeCondition = time_ns.class_("TimeHasTimeCondition", Condition) -def _load_tzdata(iana_key: str) -> Optional[bytes]: +def _load_tzdata(iana_key: str) -> bytes | None: # From https://tzdata.readthedocs.io/en/latest/#examples try: package_loc, resource = iana_key.rsplit("/", 1) diff --git a/esphome/components/time_based/time_based_cover.cpp b/esphome/components/time_based/time_based_cover.cpp index ec219d6db7..1eb591fe6e 100644 --- a/esphome/components/time_based/time_based_cover.cpp +++ b/esphome/components/time_based/time_based_cover.cpp @@ -12,8 +12,10 @@ using namespace esphome::cover; void TimeBasedCover::dump_config() { LOG_COVER("", "Time Based Cover", this); - ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f); - ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f); + ESP_LOGCONFIG(TAG, + " Open Duration: %.1fs\n" + " Close Duration: %.1fs", + this->open_duration_ / 1e3f, this->close_duration_ / 1e3f); } void TimeBasedCover::setup() { auto restore = this->restore_state_(); diff --git a/esphome/components/tlc59208f/tlc59208f_output.cpp b/esphome/components/tlc59208f/tlc59208f_output.cpp index bd62f8de6d..b1aad42bd7 100644 --- a/esphome/components/tlc59208f/tlc59208f_output.cpp +++ b/esphome/components/tlc59208f/tlc59208f_output.cpp @@ -1,7 +1,7 @@ #include "tlc59208f_output.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace tlc59208f { @@ -71,9 +71,9 @@ static const uint8_t LDR_PWM = 0x02; static const uint8_t LDR_GRPPWM = 0x03; void TLC59208FOutput::setup() { - ESP_LOGCONFIG(TAG, "Setting up TLC59208FOutputComponent..."); + ESP_LOGCONFIG(TAG, "Running setup"); - ESP_LOGV(TAG, " Resetting all devices on the bus..."); + ESP_LOGV(TAG, " Resetting all devices on the bus"); // Reset all devices on the bus if (this->bus_->write(TLC59208F_SWRST_ADDR >> 1, TLC59208F_SWRST_SEQ, 2) != i2c::ERROR_OK) { @@ -111,8 +111,10 @@ void TLC59208FOutput::setup() { } void TLC59208FOutput::dump_config() { - ESP_LOGCONFIG(TAG, "TLC59208F:"); - ESP_LOGCONFIG(TAG, " Mode: 0x%02X", this->mode_); + ESP_LOGCONFIG(TAG, + "TLC59208F:\n" + " Mode: 0x%02X", + this->mode_); if (this->is_failed()) { ESP_LOGE(TAG, "Setting up TLC59208F failed!"); diff --git a/esphome/components/tm1621/tm1621.cpp b/esphome/components/tm1621/tm1621.cpp index ebaa5a3457..502e45b35e 100644 --- a/esphome/components/tm1621/tm1621.cpp +++ b/esphome/components/tm1621/tm1621.cpp @@ -1,7 +1,7 @@ #include "tm1621.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace tm1621 { @@ -29,7 +29,7 @@ const uint8_t TM1621_DIGIT_ROW[2][12] = {{0x5F, 0x50, 0x3D, 0x79, 0x72, 0x6B, 0x {0xF5, 0x05, 0xB6, 0x97, 0x47, 0xD3, 0xF3, 0x85, 0xF7, 0xD7, 0x02, 0x00}}; void TM1621Display::setup() { - ESP_LOGCONFIG(TAG, "Setting up TM1621..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->cs_pin_->setup(); // OUTPUT this->cs_pin_->digital_write(true); diff --git a/esphome/components/tm1637/tm1637.cpp b/esphome/components/tm1637/tm1637.cpp index 2f2d4b707a..358a683efb 100644 --- a/esphome/components/tm1637/tm1637.cpp +++ b/esphome/components/tm1637/tm1637.cpp @@ -1,7 +1,7 @@ #include "tm1637.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace tm1637 { @@ -125,7 +125,7 @@ const uint8_t TM1637_ASCII_TO_RAW[] PROGMEM = { 0b01100011, // '~', ord 0x7E (degree symbol) }; void TM1637Display::setup() { - ESP_LOGCONFIG(TAG, "Setting up TM1637..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->clk_pin_->setup(); // OUTPUT this->clk_pin_->digital_write(false); // LOW @@ -135,10 +135,12 @@ void TM1637Display::setup() { this->display(); } void TM1637Display::dump_config() { - ESP_LOGCONFIG(TAG, "TM1637:"); - ESP_LOGCONFIG(TAG, " Intensity: %d", this->intensity_); - ESP_LOGCONFIG(TAG, " Inverted: %d", this->inverted_); - ESP_LOGCONFIG(TAG, " Length: %d", this->length_); + ESP_LOGCONFIG(TAG, + "TM1637:\n" + " Intensity: %d\n" + " Inverted: %d\n" + " Length: %d", + this->intensity_, this->inverted_, this->length_); LOG_PIN(" CLK Pin: ", this->clk_pin_); LOG_PIN(" DIO Pin: ", this->dio_pin_); LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/tm1638/tm1638.cpp b/esphome/components/tm1638/tm1638.cpp index a15b006046..f43b496b35 100644 --- a/esphome/components/tm1638/tm1638.cpp +++ b/esphome/components/tm1638/tm1638.cpp @@ -1,8 +1,8 @@ #include "tm1638.h" #include "sevenseg.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace tm1638 { @@ -20,7 +20,7 @@ static const uint8_t TM1638_UNKNOWN_CHAR = 0b11111111; static const uint8_t TM1638_SHIFT_DELAY = 4; // clock pause between commands, default 4ms void TM1638Component::setup() { - ESP_LOGD(TAG, "Setting up TM1638..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->clk_pin_->setup(); // OUTPUT this->dio_pin_->setup(); // OUTPUT @@ -43,8 +43,10 @@ void TM1638Component::setup() { } void TM1638Component::dump_config() { - ESP_LOGCONFIG(TAG, "TM1638:"); - ESP_LOGCONFIG(TAG, " Intensity: %u", this->intensity_); + ESP_LOGCONFIG(TAG, + "TM1638:\n" + " Intensity: %u", + this->intensity_); LOG_PIN(" CLK Pin: ", this->clk_pin_); LOG_PIN(" DIO Pin: ", this->dio_pin_); LOG_PIN(" STB Pin: ", this->stb_pin_); diff --git a/esphome/components/tm1651/tm1651.cpp b/esphome/components/tm1651/tm1651.cpp index 89807f5565..64c3e62b32 100644 --- a/esphome/components/tm1651/tm1651.cpp +++ b/esphome/components/tm1651/tm1651.cpp @@ -1,8 +1,8 @@ #ifdef USE_ARDUINO #include "tm1651.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace tm1651 { @@ -17,7 +17,7 @@ static const uint8_t TM1651_BRIGHTNESS_MEDIUM_HW = 2; static const uint8_t TM1651_BRIGHTNESS_HIGH_HW = 7; void TM1651Display::setup() { - ESP_LOGCONFIG(TAG, "Setting up TM1651..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t clk = clk_pin_->get_pin(); uint8_t dio = dio_pin_->get_pin(); diff --git a/esphome/components/tmp102/tmp102.cpp b/esphome/components/tmp102/tmp102.cpp index f35fbf5d4b..7390d9fcc9 100644 --- a/esphome/components/tmp102/tmp102.cpp +++ b/esphome/components/tmp102/tmp102.cpp @@ -15,13 +15,11 @@ static const uint8_t TMP102_REGISTER_HIGH_LIMIT = 0x03; static const float TMP102_CONVERSION_FACTOR = 0.0625; -void TMP102Component::setup() { ESP_LOGCONFIG(TAG, "Setting up TMP102..."); } - void TMP102Component::dump_config() { ESP_LOGCONFIG(TAG, "TMP102:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with TMP102 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this); diff --git a/esphome/components/tmp102/tmp102.h b/esphome/components/tmp102/tmp102.h index 1bbb2d5ae3..657b48c7cf 100644 --- a/esphome/components/tmp102/tmp102.h +++ b/esphome/components/tmp102/tmp102.h @@ -9,10 +9,7 @@ namespace tmp102 { class TMP102Component : public PollingComponent, public i2c::I2CDevice, public sensor::Sensor { public: - /// Setup (reset) the sensor and check connection. - void setup() override; void dump_config() override; - /// Update the sensor values (temperature) void update() override; float get_setup_priority() const override; diff --git a/esphome/components/tmp1075/tmp1075.cpp b/esphome/components/tmp1075/tmp1075.cpp index 68253a11f1..831f905bd2 100644 --- a/esphome/components/tmp1075/tmp1075.cpp +++ b/esphome/components/tmp1075/tmp1075.cpp @@ -44,17 +44,20 @@ void TMP1075Sensor::update() { void TMP1075Sensor::dump_config() { LOG_SENSOR("", "TMP1075 Sensor", this); if (this->is_failed()) { - ESP_LOGE(TAG, " Communication with TMP1075 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); return; } - ESP_LOGCONFIG(TAG, " limit low : %.4f °C", alert_limit_low_); - ESP_LOGCONFIG(TAG, " limit high : %.4f °C", alert_limit_high_); - ESP_LOGCONFIG(TAG, " oneshot : %d", config_.fields.oneshot); - ESP_LOGCONFIG(TAG, " rate : %d", config_.fields.rate); - ESP_LOGCONFIG(TAG, " fault_count: %d", config_.fields.faults); - ESP_LOGCONFIG(TAG, " polarity : %d", config_.fields.polarity); - ESP_LOGCONFIG(TAG, " alert_mode : %d", config_.fields.alert_mode); - ESP_LOGCONFIG(TAG, " shutdown : %d", config_.fields.shutdown); + ESP_LOGCONFIG(TAG, + " limit low : %.4f °C\n" + " limit high : %.4f °C\n" + " oneshot : %d\n" + " rate : %d\n" + " fault_count: %d\n" + " polarity : %d\n" + " alert_mode : %d\n" + " shutdown : %d", + alert_limit_low_, alert_limit_high_, config_.fields.oneshot, config_.fields.rate, config_.fields.faults, + config_.fields.polarity, config_.fields.alert_mode, config_.fields.shutdown); } void TMP1075Sensor::set_fault_count(const int faults) { diff --git a/esphome/components/tmp117/tmp117.cpp b/esphome/components/tmp117/tmp117.cpp index f719ea93b6..5fe8f51414 100644 --- a/esphome/components/tmp117/tmp117.cpp +++ b/esphome/components/tmp117/tmp117.cpp @@ -26,7 +26,7 @@ void TMP117Component::update() { } } void TMP117Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up TMP117..."); + ESP_LOGCONFIG(TAG, "Running setup"); if (!this->write_config_(this->config_)) { this->mark_failed(); @@ -43,7 +43,7 @@ void TMP117Component::dump_config() { ESP_LOGD(TAG, "TMP117:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with TMP117 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_SENSOR(" ", "Temperature", this); } diff --git a/esphome/components/tof10120/tof10120_sensor.cpp b/esphome/components/tof10120/tof10120_sensor.cpp index 32cd604be9..e27c7bbd64 100644 --- a/esphome/components/tof10120/tof10120_sensor.cpp +++ b/esphome/components/tof10120/tof10120_sensor.cpp @@ -27,7 +27,7 @@ void TOF10120Sensor::setup() {} void TOF10120Sensor::update() { if (!this->write_bytes(TOF10120_DISTANCE_REGISTER, TOF10120_READ_DISTANCE_CMD, sizeof(TOF10120_READ_DISTANCE_CMD))) { - ESP_LOGE(TAG, "Communication with TOF10120 failed on write"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning(); return; } @@ -39,7 +39,7 @@ void TOF10120Sensor::update() { } delay(TOF10120_DEFAULT_DELAY); if (this->read(data, 2) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Communication with TOF10120 failed on read"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); this->status_set_warning(); return; } diff --git a/esphome/components/tormatic/tormatic_cover.cpp b/esphome/components/tormatic/tormatic_cover.cpp index 35224c8ec7..be412d62a8 100644 --- a/esphome/components/tormatic/tormatic_cover.cpp +++ b/esphome/components/tormatic/tormatic_cover.cpp @@ -34,8 +34,10 @@ void Tormatic::dump_config() { LOG_COVER("", "Tormatic Cover", this); this->check_uart_settings(9600, 1, uart::UART_CONFIG_PARITY_NONE, 8); - ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f); - ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f); + ESP_LOGCONFIG(TAG, + " Open Duration: %.1fs\n" + " Close Duration: %.1fs", + this->open_duration_ / 1e3f, this->close_duration_ / 1e3f); auto restore = this->restore_state_(); if (restore.has_value()) { diff --git a/esphome/components/tsl2561/tsl2561.cpp b/esphome/components/tsl2561/tsl2561.cpp index e80e9220d8..1b5c9f2635 100644 --- a/esphome/components/tsl2561/tsl2561.cpp +++ b/esphome/components/tsl2561/tsl2561.cpp @@ -15,7 +15,7 @@ static const uint8_t TSL2561_REGISTER_DATA_0 = 0x0C; static const uint8_t TSL2561_REGISTER_DATA_1 = 0x0E; void TSL2561Sensor::setup() { - ESP_LOGCONFIG(TAG, "Setting up TSL2561..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t id; if (!this->tsl2561_read_byte(TSL2561_REGISTER_ID, &id)) { this->mark_failed(); @@ -41,12 +41,14 @@ void TSL2561Sensor::dump_config() { LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with TSL2561 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } int gain = this->gain_ == TSL2561_GAIN_1X ? 1 : 16; - ESP_LOGCONFIG(TAG, " Gain: %dx", gain); - ESP_LOGCONFIG(TAG, " Integration Time: %.1f ms", this->get_integration_time_ms_()); + ESP_LOGCONFIG(TAG, + " Gain: %dx\n" + " Integration Time: %.1f ms", + gain, this->get_integration_time_ms_()); LOG_UPDATE_INTERVAL(this); } @@ -65,7 +67,7 @@ void TSL2561Sensor::update() { float TSL2561Sensor::calculate_lx_(uint16_t ch0, uint16_t ch1) { if ((ch0 == 0xFFFF) || (ch1 == 0xFFFF)) { - ESP_LOGW(TAG, "TSL2561 sensor is saturated."); + ESP_LOGW(TAG, "Sensor is saturated"); return NAN; } diff --git a/esphome/components/tsl2591/tsl2591.cpp b/esphome/components/tsl2591/tsl2591.cpp index 977048364c..1734d83dd2 100644 --- a/esphome/components/tsl2591/tsl2591.cpp +++ b/esphome/components/tsl2591/tsl2591.cpp @@ -26,13 +26,13 @@ static const char *const TAG = "tsl2591.sensor"; void TSL2591Component::enable() { // Enable the device by setting the control bit to 0x01. Also turn on ADCs. if (!this->write_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_ENABLE, TSL2591_ENABLE_POWERON | TSL2591_ENABLE_AEN)) { - ESP_LOGE(TAG, "Failed I2C write during enable()"); + ESP_LOGE(TAG, "I2C write failed"); } } void TSL2591Component::disable() { if (!this->write_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_ENABLE, TSL2591_ENABLE_POWEROFF)) { - ESP_LOGE(TAG, "Failed I2C write during disable()"); + ESP_LOGE(TAG, "I2C write failed"); } } @@ -43,6 +43,7 @@ void TSL2591Component::disable_if_power_saving_() { } void TSL2591Component::setup() { + ESP_LOGCONFIG(TAG, "Running setup for address 0x%02X", this->address_); switch (this->component_gain_) { case TSL2591_CGAIN_LOW: this->gain_ = TSL2591_GAIN_LOW; @@ -61,21 +62,15 @@ void TSL2591Component::setup() { break; } - uint8_t address = this->address_; - ESP_LOGI(TAG, "Setting up TSL2591 sensor at I2C address 0x%02X", address); - uint8_t id; if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_DEVICE_ID, &id)) { - ESP_LOGE(TAG, "Failed I2C read during setup()"); + ESP_LOGE(TAG, "I2C read failed"); this->mark_failed(); return; } if (id != 0x50) { - ESP_LOGE(TAG, - "Could not find the TSL2591 sensor. The ID register of the device at address 0x%02X reported 0x%02X " - "instead of 0x50.", - address, id); + ESP_LOGE(TAG, "Unknown chip ID"); this->mark_failed(); return; } @@ -89,7 +84,7 @@ void TSL2591Component::dump_config() { LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with TSL2591 failed earlier, during setup"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); return; } @@ -119,13 +114,16 @@ void TSL2591Component::dump_config() { gain_word = "auto"; break; } - ESP_LOGCONFIG(TAG, " Gain: %dx (%s)", gain, gain_word.c_str()); TSL2591IntegrationTime raw_timing = this->integration_time_; int timing_ms = (1 + raw_timing) * 100; - ESP_LOGCONFIG(TAG, " Integration Time: %d ms", timing_ms); - ESP_LOGCONFIG(TAG, " Power save mode enabled: %s", ONOFF(this->power_save_mode_enabled_)); - ESP_LOGCONFIG(TAG, " Device factor: %f", this->device_factor_); - ESP_LOGCONFIG(TAG, " Glass attenuation factor: %f", this->glass_attenuation_factor_); + ESP_LOGCONFIG(TAG, + " Gain: %dx (%s)" + " Integration Time: %d ms\n" + " Power save mode enabled: %s\n" + " Device factor: %f\n" + " Glass attenuation factor: %f", + gain, gain_word.c_str(), timing_ms, ONOFF(this->power_save_mode_enabled_), this->device_factor_, + this->glass_attenuation_factor_); LOG_SENSOR(" ", "Full spectrum:", this->full_spectrum_sensor_); LOG_SENSOR(" ", "Infrared:", this->infrared_sensor_); LOG_SENSOR(" ", "Visible:", this->visible_sensor_); @@ -174,7 +172,7 @@ void TSL2591Component::interval_function_for_update_() { uint64_t now = millis(); ESP_LOGD(TAG, "Elapsed %3llu ms; still waiting for valid ADC", (now - this->interval_start_)); if (now > this->interval_timeout_) { - ESP_LOGW(TAG, "Interval timeout for TSL2591 '%s' expired before ADCs became valid.", this->name_); + ESP_LOGW(TAG, "Interval timeout for '%s' expired before ADCs became valid", this->name_); this->cancel_interval(interval_name); } return; @@ -235,7 +233,7 @@ void TSL2591Component::set_integration_time_and_gain(TSL2591IntegrationTime inte this->gain_ = gain; if (!this->write_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CONTROL, this->integration_time_ | this->gain_)) { // NOLINT - ESP_LOGE(TAG, "Failed I2C write during set_integration_time_and_gain()"); + ESP_LOGE(TAG, "I2C write failed"); } // The ADC values can be confused if gain or integration time are changed in the middle of a cycle. // So, we unconditionally disable the device to turn the ADCs off. When re-enabling, the ADCs @@ -255,7 +253,7 @@ float TSL2591Component::get_setup_priority() const { return setup_priority::DATA bool TSL2591Component::is_adc_valid() { uint8_t status; if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_STATUS, &status)) { - ESP_LOGE(TAG, "Failed I2C read during is_adc_valid()"); + ESP_LOGE(TAG, "I2C read failed"); return false; } return status & 0x01; @@ -281,7 +279,7 @@ uint32_t TSL2591Component::get_combined_illuminance() { if (!avalid) { // still not valid after a sutiable delay // we don't mark the device as failed since it might come around in the future (probably not :-() - ESP_LOGE(TAG, "tsl2591 device '%s' did not return valid readings.", this->name_); + ESP_LOGE(TAG, "Device '%s' returned invalid readings", this->name_); this->disable_if_power_saving_(); return 0; } @@ -295,20 +293,20 @@ uint32_t TSL2591Component::get_combined_illuminance() { uint16_t ch0_16; uint16_t ch1_16; if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CHAN0_LOW, &ch0low)) { - ESP_LOGE(TAG, "Failed I2C read during get_combined_illuminance()"); + ESP_LOGE(TAG, "I2C read failed"); return 0; } if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CHAN0_HIGH, &ch0high)) { - ESP_LOGE(TAG, "Failed I2C read during get_combined_illuminance()"); + ESP_LOGE(TAG, "I2C read failed"); return 0; } ch0_16 = (ch0high << 8) | ch0low; if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CHAN1_LOW, &ch1low)) { - ESP_LOGE(TAG, "Failed I2C read during get_combined_illuminance()"); + ESP_LOGE(TAG, "I2C read failed"); return 0; } if (!this->read_byte(TSL2591_COMMAND_BIT | TSL2591_REGISTER_CHAN1_HIGH, &ch1high)) { - ESP_LOGE(TAG, "Failed I2C read during get_combined_illuminance()"); + ESP_LOGE(TAG, "I2C read failed"); return 0; } ch1_16 = (ch1high << 8) | ch1low; @@ -335,7 +333,7 @@ uint16_t TSL2591Component::get_illuminance(TSL2591SensorChannel channel, uint32_ return ((combined_illuminance & 0xFFFF) - (combined_illuminance >> 16)); } // unknown channel! - ESP_LOGE(TAG, "TSL2591Component::get_illuminance() caller requested an unknown channel: %d", channel); + ESP_LOGE(TAG, "get_illuminance() caller requested an unknown channel: %d", channel); return 0; } @@ -358,13 +356,13 @@ float TSL2591Component::get_calculated_lux(uint16_t full_spectrum, uint16_t infr uint16_t max_count = (this->integration_time_ == TSL2591_INTEGRATION_TIME_100MS ? 36863 : 65535); if ((full_spectrum == max_count) || (infrared == max_count)) { // Signal an overflow - ESP_LOGW(TAG, "Apparent saturation on TSL2591 (%s). You could reduce the gain or integration time.", this->name_); + ESP_LOGW(TAG, "Apparent saturation on '%s'; try reducing the gain or integration time", this->name_); return NAN; } if ((full_spectrum == 0) && (infrared == 0)) { // trivial conversion; avoids divide by 0 - ESP_LOGW(TAG, "Zero reading on both TSL2591 (%s) sensors. Is the device having a problem?", this->name_); + ESP_LOGW(TAG, "Zero reading on both '%s' sensors", this->name_); return 0.0F; } diff --git a/esphome/components/tt21100/touchscreen/tt21100.cpp b/esphome/components/tt21100/touchscreen/tt21100.cpp index d18ce835c1..d4dd1c195f 100644 --- a/esphome/components/tt21100/touchscreen/tt21100.cpp +++ b/esphome/components/tt21100/touchscreen/tt21100.cpp @@ -47,7 +47,7 @@ struct TT21100TouchReport { float TT21100Touchscreen::get_setup_priority() const { return setup_priority::HARDWARE - 1.0f; } void TT21100Touchscreen::setup() { - ESP_LOGCONFIG(TAG, "Setting up TT21100 Touchscreen..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Register interrupt pin if (this->interrupt_pin_ != nullptr) { diff --git a/esphome/components/ttp229_bsf/ttp229_bsf.cpp b/esphome/components/ttp229_bsf/ttp229_bsf.cpp index 93c947d28a..8b58795ebb 100644 --- a/esphome/components/ttp229_bsf/ttp229_bsf.cpp +++ b/esphome/components/ttp229_bsf/ttp229_bsf.cpp @@ -7,7 +7,7 @@ namespace ttp229_bsf { static const char *const TAG = "ttp229_bsf"; void TTP229BSFComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up ttp229_bsf... "); + ESP_LOGCONFIG(TAG, "Running setup"); this->sdo_pin_->setup(); this->scl_pin_->setup(); this->scl_pin_->digital_write(true); diff --git a/esphome/components/ttp229_lsf/ttp229_lsf.cpp b/esphome/components/ttp229_lsf/ttp229_lsf.cpp index 773d51b76e..8e976da4ef 100644 --- a/esphome/components/ttp229_lsf/ttp229_lsf.cpp +++ b/esphome/components/ttp229_lsf/ttp229_lsf.cpp @@ -7,7 +7,7 @@ namespace ttp229_lsf { static const char *const TAG = "ttp229_lsf"; void TTP229LSFComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up ttp229..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t data[2]; if (this->read(data, 2) != i2c::ERROR_OK) { this->error_code_ = COMMUNICATION_FAILED; @@ -20,7 +20,7 @@ void TTP229LSFComponent::dump_config() { LOG_I2C_DEVICE(this); switch (this->error_code_) { case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Communication with TTP229 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); break; case NONE: default: diff --git a/esphome/components/tuya/select/tuya_select.cpp b/esphome/components/tuya/select/tuya_select.cpp index 02643e97f4..07b0ff2815 100644 --- a/esphome/components/tuya/select/tuya_select.cpp +++ b/esphome/components/tuya/select/tuya_select.cpp @@ -44,9 +44,11 @@ void TuyaSelect::control(const std::string &value) { void TuyaSelect::dump_config() { LOG_SELECT("", "Tuya Select", this); - ESP_LOGCONFIG(TAG, " Select has datapoint ID %u", this->select_id_); - ESP_LOGCONFIG(TAG, " Data type: %s", this->is_int_ ? "int" : "enum"); - ESP_LOGCONFIG(TAG, " Options are:"); + ESP_LOGCONFIG(TAG, + " Select has datapoint ID %u\n" + " Data type: %s\n" + " Options are:", + this->select_id_, this->is_int_ ? "int" : "enum"); auto options = this->traits.get_options(); for (auto i = 0; i < this->mappings_.size(); i++) { ESP_LOGCONFIG(TAG, " %i: %s", this->mappings_.at(i), options.at(i).c_str()); diff --git a/esphome/components/tx20/tx20.cpp b/esphome/components/tx20/tx20.cpp index a2a3baa46e..73b865e8b8 100644 --- a/esphome/components/tx20/tx20.cpp +++ b/esphome/components/tx20/tx20.cpp @@ -1,6 +1,6 @@ #include "tx20.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include @@ -15,7 +15,7 @@ static const char *const DIRECTIONS[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "S "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"}; void Tx20Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up Tx20"); + ESP_LOGCONFIG(TAG, "Running setup"); this->pin_->setup(); this->store_.buffer = new uint16_t[MAX_BUFFER_SIZE]; @@ -44,7 +44,7 @@ float Tx20Component::get_setup_priority() const { return setup_priority::DATA; } std::string Tx20Component::get_wind_cardinal_direction() const { return this->wind_cardinal_direction_; } void Tx20Component::decode_and_publish_() { - ESP_LOGVV(TAG, "Decode Tx20..."); + ESP_LOGVV(TAG, "Decode Tx20"); std::string string_buffer; std::string string_buffer_2; diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index bee037774f..a0908a299c 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -1,5 +1,4 @@ import re -from typing import Optional from esphome import automation, pins import esphome.codegen as cg @@ -322,12 +321,12 @@ def final_validate_device_schema( name: str, *, uart_bus: str = CONF_UART_ID, - baud_rate: Optional[int] = None, + baud_rate: int | None = None, require_tx: bool = False, require_rx: bool = False, - data_bits: Optional[int] = None, - parity: Optional[str] = None, - stop_bits: Optional[int] = None, + data_bits: int | None = None, + parity: str | None = None, + stop_bits: int | None = None, ): def validate_baud_rate(value): if value != baud_rate: diff --git a/esphome/components/uart/button/uart_button.cpp b/esphome/components/uart/button/uart_button.cpp index 4db164c400..dd228b9bb7 100644 --- a/esphome/components/uart/button/uart_button.cpp +++ b/esphome/components/uart/button/uart_button.cpp @@ -7,7 +7,7 @@ namespace uart { static const char *const TAG = "uart.button"; void UARTButton::press_action() { - ESP_LOGD(TAG, "'%s': Sending data...", this->get_name().c_str()); + ESP_LOGD(TAG, "'%s': Sending data", this->get_name().c_str()); this->write_array(this->data_.data(), this->data_.size()); } diff --git a/esphome/components/uart/switch/uart_switch.cpp b/esphome/components/uart/switch/uart_switch.cpp index 96f50ff50f..4f5ff9fc99 100644 --- a/esphome/components/uart/switch/uart_switch.cpp +++ b/esphome/components/uart/switch/uart_switch.cpp @@ -19,11 +19,11 @@ void UARTSwitch::loop() { void UARTSwitch::write_command_(bool state) { if (state && !this->data_on_.empty()) { - ESP_LOGD(TAG, "'%s': Sending on data...", this->get_name().c_str()); + ESP_LOGD(TAG, "'%s': Sending on data", this->get_name().c_str()); this->write_array(this->data_on_.data(), this->data_on_.size()); } if (!state && !this->data_off_.empty()) { - ESP_LOGD(TAG, "'%s': Sending off data...", this->get_name().c_str()); + ESP_LOGD(TAG, "'%s': Sending off data", this->get_name().c_str()); this->write_array(this->data_off_.data(), this->data_off_.size()); } } diff --git a/esphome/components/uart/uart.cpp b/esphome/components/uart/uart.cpp index 9834462ff9..b18454bf9d 100644 --- a/esphome/components/uart/uart.cpp +++ b/esphome/components/uart/uart.cpp @@ -1,8 +1,8 @@ #include "uart.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/application.h" #include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include namespace esphome { diff --git a/esphome/components/uart/uart_component_esp32_arduino.cpp b/esphome/components/uart/uart_component_esp32_arduino.cpp index b241c03de4..7441d8c1b3 100644 --- a/esphome/components/uart/uart_component_esp32_arduino.cpp +++ b/esphome/components/uart/uart_component_esp32_arduino.cpp @@ -74,7 +74,7 @@ uint32_t ESP32ArduinoUARTComponent::get_config() { } void ESP32ArduinoUARTComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up UART..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Use Arduino HardwareSerial UARTs if all used pins match the ones // preconfigured by the platform. For example if RX disabled but TX pin // is 1 we still want to use Serial. @@ -154,10 +154,12 @@ void ESP32ArduinoUARTComponent::dump_config() { if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); } - ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); - ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); + ESP_LOGCONFIG(TAG, + " Baud Rate: %u baud\n" + " Data Bits: %u\n" + " Parity: %s\n" + " Stop bits: %u", + this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_); this->check_logger_conflict(); } @@ -191,7 +193,7 @@ bool ESP32ArduinoUARTComponent::read_array(uint8_t *data, size_t len) { int ESP32ArduinoUARTComponent::available() { return this->hw_serial_->available(); } void ESP32ArduinoUARTComponent::flush() { - ESP_LOGVV(TAG, " Flushing..."); + ESP_LOGVV(TAG, " Flushing"); this->hw_serial_->flush(); } diff --git a/esphome/components/uart/uart_component_esp8266.cpp b/esphome/components/uart/uart_component_esp8266.cpp index fa8dc3fb17..7f4cc7b37c 100644 --- a/esphome/components/uart/uart_component_esp8266.cpp +++ b/esphome/components/uart/uart_component_esp8266.cpp @@ -56,7 +56,7 @@ uint32_t ESP8266UartComponent::get_config() { } void ESP8266UartComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up UART bus..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Use Arduino HardwareSerial UARTs if all used pins match the ones // preconfigured by the platform. For example if RX disabled but TX pin // is 1 we still want to use Serial. @@ -99,7 +99,7 @@ void ESP8266UartComponent::setup() { } void ESP8266UartComponent::load_settings(bool dump_config) { - ESP_LOGCONFIG(TAG, "Loading UART bus settings..."); + ESP_LOGCONFIG(TAG, "Loading UART bus settings"); if (this->hw_serial_ != nullptr) { SerialConfig config = static_cast(get_config()); this->hw_serial_->begin(this->baud_rate_, config); @@ -121,10 +121,12 @@ void ESP8266UartComponent::dump_config() { if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); // NOLINT } - ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); - ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); + ESP_LOGCONFIG(TAG, + " Baud Rate: %u baud\n" + " Data Bits: %u\n" + " Parity: %s\n" + " Stop bits: %u", + this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_); if (this->hw_serial_ != nullptr) { ESP_LOGCONFIG(TAG, " Using hardware serial interface."); } else { @@ -193,7 +195,7 @@ int ESP8266UartComponent::available() { } } void ESP8266UartComponent::flush() { - ESP_LOGVV(TAG, " Flushing..."); + ESP_LOGVV(TAG, " Flushing"); if (this->hw_serial_ != nullptr) { this->hw_serial_->flush(); } else { diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp index 122d4105c8..84bd48d530 100644 --- a/esphome/components/uart/uart_component_esp_idf.cpp +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -85,12 +85,12 @@ void IDFUARTComponent::setup() { #endif // USE_LOGGER if (next_uart_num >= SOC_UART_NUM) { - ESP_LOGW(TAG, "Maximum number of UART components created already."); + ESP_LOGW(TAG, "Maximum number of UART components created already"); this->mark_failed(); return; } this->uart_num_ = static_cast(next_uart_num++); - ESP_LOGCONFIG(TAG, "Setting up UART %u...", this->uart_num_); + ESP_LOGCONFIG(TAG, "Running setup for UART %u", this->uart_num_); this->lock_ = xSemaphoreCreateMutex(); @@ -162,10 +162,12 @@ void IDFUARTComponent::dump_config() { if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); } - ESP_LOGCONFIG(TAG, " Baud Rate: %" PRIu32 " baud", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); - ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); + ESP_LOGCONFIG(TAG, + " Baud Rate: %" PRIu32 " baud\n" + " Data Bits: %u\n" + " Parity: %s\n" + " Stop bits: %u", + this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_); this->check_logger_conflict(); } @@ -234,7 +236,7 @@ int IDFUARTComponent::available() { } void IDFUARTComponent::flush() { - ESP_LOGVV(TAG, " Flushing..."); + ESP_LOGVV(TAG, " Flushing"); xSemaphoreTake(this->lock_, portMAX_DELAY); uart_wait_tx_done(this->uart_num_, portMAX_DELAY); xSemaphoreGive(this->lock_); diff --git a/esphome/components/uart/uart_component_host.cpp b/esphome/components/uart/uart_component_host.cpp index 40d3e91ab2..adb11266c5 100644 --- a/esphome/components/uart/uart_component_host.cpp +++ b/esphome/components/uart/uart_component_host.cpp @@ -109,7 +109,7 @@ HostUartComponent::~HostUartComponent() { } void HostUartComponent::setup() { - ESP_LOGCONFIG(TAG, "Opening UART port..."); + ESP_LOGCONFIG(TAG, "Opening UART port"); speed_t baud = get_baud(this->baud_rate_); if (baud == B0) { ESP_LOGE(TAG, "Unsupported baud rate: %d", this->baud_rate_); @@ -188,14 +188,17 @@ void HostUartComponent::dump_config() { } return; } - ESP_LOGCONFIG(TAG, " Port status: opened"); - ESP_LOGCONFIG(TAG, " Baud Rate: %d", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data Bits: %d", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", + ESP_LOGCONFIG(TAG, + " Port status: opened\n" + " Baud Rate: %d\n" + " Data Bits: %d\n" + " Parity: %s\n" + " Stop Bits: %d", + this->baud_rate_, this->data_bits_, this->parity_ == UART_CONFIG_PARITY_NONE ? "None" : this->parity_ == UART_CONFIG_PARITY_EVEN ? "Even" - : "Odd"); - ESP_LOGCONFIG(TAG, " Stop Bits: %d", this->stop_bits_); + : "Odd", + this->stop_bits_); this->check_logger_conflict(); } @@ -283,7 +286,7 @@ void HostUartComponent::flush() { return; } tcflush(this->file_descriptor_, TCIOFLUSH); - ESP_LOGV(TAG, " Flushing..."); + ESP_LOGV(TAG, " Flushing"); } void HostUartComponent::update_error_(const std::string &error) { diff --git a/esphome/components/uart/uart_component_libretiny.cpp b/esphome/components/uart/uart_component_libretiny.cpp index c5e299e9d1..ffdb329669 100644 --- a/esphome/components/uart/uart_component_libretiny.cpp +++ b/esphome/components/uart/uart_component_libretiny.cpp @@ -46,7 +46,7 @@ uint16_t LibreTinyUARTComponent::get_config() { } void LibreTinyUARTComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up UART..."); + ESP_LOGCONFIG(TAG, "Running setup"); int8_t tx_pin = tx_pin_ == nullptr ? -1 : tx_pin_->get_pin(); int8_t rx_pin = rx_pin_ == nullptr ? -1 : rx_pin_->get_pin(); @@ -108,10 +108,12 @@ void LibreTinyUARTComponent::dump_config() { if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); } - ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); - ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); + ESP_LOGCONFIG(TAG, + " Baud Rate: %u baud\n" + " Data Bits: %u\n" + " Parity: %s\n" + " Stop bits: %u", + this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_); this->check_logger_conflict(); } @@ -145,7 +147,7 @@ bool LibreTinyUARTComponent::read_array(uint8_t *data, size_t len) { int LibreTinyUARTComponent::available() { return this->serial_->available(); } void LibreTinyUARTComponent::flush() { - ESP_LOGVV(TAG, " Flushing..."); + ESP_LOGVV(TAG, " Flushing"); this->serial_->flush(); } diff --git a/esphome/components/uart/uart_component_rp2040.cpp b/esphome/components/uart/uart_component_rp2040.cpp index e2c47080ac..f375d4a93f 100644 --- a/esphome/components/uart/uart_component_rp2040.cpp +++ b/esphome/components/uart/uart_component_rp2040.cpp @@ -52,7 +52,7 @@ uint16_t RP2040UartComponent::get_config() { } void RP2040UartComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up UART bus..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint16_t config = get_config(); @@ -136,10 +136,12 @@ void RP2040UartComponent::dump_config() { if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); } - ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); - ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); + ESP_LOGCONFIG(TAG, + " Baud Rate: %u baud\n" + " Data Bits: %u\n" + " Parity: %s\n" + " Stop bits: %u", + this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_); if (this->hw_serial_) { ESP_LOGCONFIG(TAG, " Using hardware serial"); } else { @@ -174,7 +176,7 @@ bool RP2040UartComponent::read_array(uint8_t *data, size_t len) { } int RP2040UartComponent::available() { return this->serial_->available(); } void RP2040UartComponent::flush() { - ESP_LOGVV(TAG, " Flushing..."); + ESP_LOGVV(TAG, " Flushing"); this->serial_->flush(); } diff --git a/esphome/components/udp/udp_component.cpp b/esphome/components/udp/udp_component.cpp index 222c73f82e..62a1189355 100644 --- a/esphome/components/udp/udp_component.cpp +++ b/esphome/components/udp/udp_component.cpp @@ -122,16 +122,20 @@ void UDPComponent::loop() { } void UDPComponent::dump_config() { - ESP_LOGCONFIG(TAG, "UDP:"); - ESP_LOGCONFIG(TAG, " Listen Port: %u", this->listen_port_); - ESP_LOGCONFIG(TAG, " Broadcast Port: %u", this->broadcast_port_); + ESP_LOGCONFIG(TAG, + "UDP:\n" + " Listen Port: %u\n" + " Broadcast Port: %u", + this->listen_port_, this->broadcast_port_); for (const auto &address : this->addresses_) ESP_LOGCONFIG(TAG, " Address: %s", address.c_str()); if (this->listen_address_.has_value()) { ESP_LOGCONFIG(TAG, " Listen address: %s", this->listen_address_.value().str().c_str()); } - ESP_LOGCONFIG(TAG, " Broadcasting: %s", YESNO(this->should_broadcast_)); - ESP_LOGCONFIG(TAG, " Listening: %s", YESNO(this->should_listen_)); + ESP_LOGCONFIG(TAG, + " Broadcasting: %s\n" + " Listening: %s", + YESNO(this->should_broadcast_), YESNO(this->should_listen_)); } void UDPComponent::send_packet(const uint8_t *data, size_t size) { diff --git a/esphome/components/ufire_ec/ufire_ec.cpp b/esphome/components/ufire_ec/ufire_ec.cpp index 7af4fadf75..813e667a00 100644 --- a/esphome/components/ufire_ec/ufire_ec.cpp +++ b/esphome/components/ufire_ec/ufire_ec.cpp @@ -7,7 +7,7 @@ namespace ufire_ec { static const char *const TAG = "ufire_ec"; void UFireECComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up uFire_ec..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t version; if (!this->read_byte(REGISTER_VERSION, &version) && version != 0xFF) { @@ -110,8 +110,10 @@ void UFireECComponent::dump_config() { LOG_SENSOR(" ", "EC Sensor", this->ec_sensor_) LOG_SENSOR(" ", "Temperature Sensor", this->temperature_sensor_) LOG_SENSOR(" ", "Temperature Sensor external", this->temperature_sensor_external_) - ESP_LOGCONFIG(TAG, " Temperature Compensation: %f", this->temperature_compensation_); - ESP_LOGCONFIG(TAG, " Temperature Coefficient: %f", this->temperature_coefficient_); + ESP_LOGCONFIG(TAG, + " Temperature Compensation: %f\n" + " Temperature Coefficient: %f", + this->temperature_compensation_, this->temperature_coefficient_); } } // namespace ufire_ec diff --git a/esphome/components/ufire_ise/ufire_ise.cpp b/esphome/components/ufire_ise/ufire_ise.cpp index 957e6f3299..5d0cb6ec2f 100644 --- a/esphome/components/ufire_ise/ufire_ise.cpp +++ b/esphome/components/ufire_ise/ufire_ise.cpp @@ -9,7 +9,7 @@ namespace ufire_ise { static const char *const TAG = "ufire_ise"; void UFireISEComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up uFire_ise..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t version; if (!this->read_byte(REGISTER_VERSION, &version) && version != 0xFF) { diff --git a/esphome/components/ultrasonic/ultrasonic_sensor.cpp b/esphome/components/ultrasonic/ultrasonic_sensor.cpp index 604e78d6f8..b737dfa4cd 100644 --- a/esphome/components/ultrasonic/ultrasonic_sensor.cpp +++ b/esphome/components/ultrasonic/ultrasonic_sensor.cpp @@ -8,7 +8,7 @@ namespace ultrasonic { static const char *const TAG = "ultrasonic.sensor"; void UltrasonicSensorComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up Ultrasonic Sensor..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->trigger_pin_->setup(); this->trigger_pin_->digital_write(false); this->echo_pin_->setup(); @@ -45,8 +45,10 @@ void UltrasonicSensorComponent::dump_config() { LOG_SENSOR("", "Ultrasonic Sensor", this); LOG_PIN(" Echo Pin: ", this->echo_pin_); LOG_PIN(" Trigger Pin: ", this->trigger_pin_); - ESP_LOGCONFIG(TAG, " Pulse time: %" PRIu32 " µs", this->pulse_time_us_); - ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 " µs", this->timeout_us_); + ESP_LOGCONFIG(TAG, + " Pulse time: %" PRIu32 " µs\n" + " Timeout: %" PRIu32 " µs", + this->pulse_time_us_, this->timeout_us_); LOG_UPDATE_INTERVAL(this); } float UltrasonicSensorComponent::us_to_m(uint32_t us) { diff --git a/esphome/components/update/__init__.py b/esphome/components/update/__init__.py index c2654520fd..09b0698903 100644 --- a/esphome/components/update/__init__.py +++ b/esphome/components/update/__init__.py @@ -111,6 +111,7 @@ async def register_update(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_update(var)) + CORE.register_platform_component("update", var) await setup_update_core_(var, config) diff --git a/esphome/components/update/update_entity.cpp b/esphome/components/update/update_entity.cpp index ed9a0480d8..ce97fb1b77 100644 --- a/esphome/components/update/update_entity.cpp +++ b/esphome/components/update/update_entity.cpp @@ -30,7 +30,7 @@ void UpdateEntity::publish_state() { ESP_LOGD(TAG, " Progress: %.0f%%", this->update_info_.progress); } - this->has_state_ = true; + this->set_has_state(true); this->state_callback_.call(); } diff --git a/esphome/components/update/update_entity.h b/esphome/components/update/update_entity.h index cc269e288f..169e580457 100644 --- a/esphome/components/update/update_entity.h +++ b/esphome/components/update/update_entity.h @@ -28,8 +28,6 @@ enum UpdateState : uint8_t { class UpdateEntity : public EntityBase, public EntityBase_DeviceClass { public: - bool has_state() const { return this->has_state_; } - void publish_state(); void perform() { this->perform(false); } @@ -44,7 +42,6 @@ class UpdateEntity : public EntityBase, public EntityBase_DeviceClass { protected: UpdateState state_{UPDATE_STATE_UNKNOWN}; UpdateInfo update_info_; - bool has_state_{false}; CallbackManager state_callback_{}; }; diff --git a/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp b/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp index cc9b8a0f90..d7e672d8cf 100644 --- a/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +++ b/esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp @@ -1,7 +1,7 @@ #include "uponor_smatrix_climate.h" +#include "esphome/core/application.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/application.h" namespace esphome { namespace uponor_smatrix { diff --git a/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp b/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp index 47ff3a17f5..452660dc14 100644 --- a/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +++ b/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp @@ -7,8 +7,10 @@ namespace uponor_smatrix { static const char *const TAG = "uponor_smatrix.sensor"; void UponorSmatrixSensor::dump_config() { - ESP_LOGCONFIG(TAG, "Uponor Smatrix Sensor"); - ESP_LOGCONFIG(TAG, " Device address: 0x%04X", this->address_); + ESP_LOGCONFIG(TAG, + "Uponor Smatrix Sensor\n" + " Device address: 0x%04X", + this->address_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "External Temperature", this->external_temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); diff --git a/esphome/components/uponor_smatrix/uponor_smatrix.cpp b/esphome/components/uponor_smatrix/uponor_smatrix.cpp index 2dbbef72ab..a0017518bf 100644 --- a/esphome/components/uponor_smatrix/uponor_smatrix.cpp +++ b/esphome/components/uponor_smatrix/uponor_smatrix.cpp @@ -1,6 +1,7 @@ #include "uponor_smatrix.h" -#include "esphome/core/log.h" #include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace uponor_smatrix { diff --git a/esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp b/esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp index fa8cb2bb61..69033be11c 100644 --- a/esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp +++ b/esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp @@ -13,7 +13,7 @@ static const char *const TAG = "uptime.sensor"; void UptimeTimestampSensor::setup() { this->time_->add_on_time_sync_callback([this]() { - if (this->has_state_) + if (this->has_state()) return; // No need to update the timestamp if it's already set auto now = this->time_->now(); diff --git a/esphome/components/usb_host/__init__.py b/esphome/components/usb_host/__init__.py new file mode 100644 index 0000000000..b6ca779706 --- /dev/null +++ b/esphome/components/usb_host/__init__.py @@ -0,0 +1,64 @@ +import esphome.codegen as cg +from esphome.components.esp32 import ( + VARIANT_ESP32S2, + VARIANT_ESP32S3, + add_idf_sdkconfig_option, + only_on_variant, +) +import esphome.config_validation as cv +from esphome.const import CONF_ID +from esphome.cpp_types import Component + +AUTO_LOAD = ["bytebuffer"] +CODEOWNERS = ["@clydebarrow"] +DEPENDENCIES = ["esp32"] +usb_host_ns = cg.esphome_ns.namespace("usb_host") +USBHost = usb_host_ns.class_("USBHost", Component) +USBClient = usb_host_ns.class_("USBClient", Component) + +CONF_DEVICES = "devices" +CONF_VID = "vid" +CONF_PID = "pid" + + +def usb_device_schema(cls=USBClient, vid: int = None, pid: [int] = None) -> cv.Schema: + schema = cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(cls), + } + ) + if vid: + schema = schema.extend({cv.Optional(CONF_VID, default=vid): cv.hex_uint16_t}) + else: + schema = schema.extend({cv.Required(CONF_VID): cv.hex_uint16_t}) + if pid: + schema = schema.extend({cv.Optional(CONF_PID, default=pid): cv.hex_uint16_t}) + else: + schema = schema.extend({cv.Required(CONF_PID): cv.hex_uint16_t}) + return schema + + +CONFIG_SCHEMA = cv.All( + cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(USBHost), + cv.Optional(CONF_DEVICES): cv.ensure_list(usb_device_schema()), + } + ), + cv.only_with_esp_idf, + only_on_variant(supported=[VARIANT_ESP32S2, VARIANT_ESP32S3]), +) + + +async def register_usb_client(config): + var = cg.new_Pvariable(config[CONF_ID], config[CONF_VID], config[CONF_PID]) + await cg.register_component(var, config) + return var + + +async def to_code(config): + add_idf_sdkconfig_option("CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE", 1024) + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + for device in config.get(CONF_DEVICES) or (): + await register_usb_client(device) diff --git a/esphome/components/usb_host/usb_host.h b/esphome/components/usb_host/usb_host.h new file mode 100644 index 0000000000..c5466eb1f0 --- /dev/null +++ b/esphome/components/usb_host/usb_host.h @@ -0,0 +1,116 @@ +#pragma once + +// Should not be needed, but it's required to pass CI clang-tidy checks +#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#include "esphome/core/component.h" +#include +#include "usb/usb_host.h" + +#include + +namespace esphome { +namespace usb_host { + +static const char *const TAG = "usb_host"; + +// constants for setup packet type +static const uint8_t USB_RECIP_DEVICE = 0; +static const uint8_t USB_RECIP_INTERFACE = 1; +static const uint8_t USB_RECIP_ENDPOINT = 2; +static const uint8_t USB_TYPE_STANDARD = 0 << 5; +static const uint8_t USB_TYPE_CLASS = 1 << 5; +static const uint8_t USB_TYPE_VENDOR = 2 << 5; +static const uint8_t USB_DIR_MASK = 1 << 7; +static const uint8_t USB_DIR_IN = 1 << 7; +static const uint8_t USB_DIR_OUT = 0; +static const size_t SETUP_PACKET_SIZE = 8; + +static const size_t MAX_REQUESTS = 16; // maximum number of outstanding requests possible. + +// used to report a transfer status +struct TransferStatus { + bool success; + uint16_t error_code; + uint8_t *data; + size_t data_len; + uint8_t endpoint; + void *user_data; +}; + +using transfer_cb_t = std::function; + +class USBClient; + +// struct used to capture all data needed for a transfer +struct TransferRequest { + usb_transfer_t *transfer; + transfer_cb_t callback; + TransferStatus status; + USBClient *client; +}; + +// callback function type. + +enum ClientState { + USB_CLIENT_INIT = 0, + USB_CLIENT_OPEN, + USB_CLIENT_CLOSE, + USB_CLIENT_GET_DESC, + USB_CLIENT_GET_INFO, + USB_CLIENT_CONNECTED, +}; +class USBClient : public Component { + friend class USBHost; + + public: + USBClient(uint16_t vid, uint16_t pid) : vid_(vid), pid_(pid) { init_pool(); } + + void init_pool() { + this->trq_pool_.clear(); + for (size_t i = 0; i != MAX_REQUESTS; i++) + this->trq_pool_.push_back(&this->requests_[i]); + } + void setup() override; + void loop() override; + // setup must happen after the host bus has been setup + float get_setup_priority() const override { return setup_priority::IO; } + void on_opened(uint8_t addr); + void on_removed(usb_device_handle_t handle); + void control_transfer_callback(const usb_transfer_t *xfer) const; + void transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length); + void transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length); + void dump_config() override; + void release_trq(TransferRequest *trq); + bool control_transfer(uint8_t type, uint8_t request, uint16_t value, uint16_t index, const transfer_cb_t &callback, + const std::vector &data = {}); + + protected: + bool register_(); + TransferRequest *get_trq_(); + virtual void disconnect(); + virtual void on_connected() {} + virtual void on_disconnected() { this->init_pool(); } + + usb_host_client_handle_t handle_{}; + usb_device_handle_t device_handle_{}; + int device_addr_{-1}; + int state_{USB_CLIENT_INIT}; + uint16_t vid_{}; + uint16_t pid_{}; + std::list trq_pool_{}; + TransferRequest requests_[MAX_REQUESTS]{}; +}; +class USBHost : public Component { + public: + float get_setup_priority() const override { return setup_priority::BUS; } + void loop() override; + void setup() override; + + protected: + std::vector clients_{}; +}; + +} // namespace usb_host +} // namespace esphome + +#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 diff --git a/esphome/components/usb_host/usb_host_client.cpp b/esphome/components/usb_host/usb_host_client.cpp new file mode 100644 index 0000000000..fc5f772f41 --- /dev/null +++ b/esphome/components/usb_host/usb_host_client.cpp @@ -0,0 +1,394 @@ +// Should not be needed, but it's required to pass CI clang-tidy checks +#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#include "usb_host.h" +#include "esphome/core/log.h" +#include "esphome/core/hal.h" +#include "esphome/components/bytebuffer/bytebuffer.h" + +#include +#include +namespace esphome { +namespace usb_host { + +#pragma GCC diagnostic ignored "-Wparentheses" + +using namespace bytebuffer; + +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE +static void print_ep_desc(const usb_ep_desc_t *ep_desc) { + const char *ep_type_str; + int type = ep_desc->bmAttributes & USB_BM_ATTRIBUTES_XFERTYPE_MASK; + + switch (type) { + case USB_BM_ATTRIBUTES_XFER_CONTROL: + ep_type_str = "CTRL"; + break; + case USB_BM_ATTRIBUTES_XFER_ISOC: + ep_type_str = "ISOC"; + break; + case USB_BM_ATTRIBUTES_XFER_BULK: + ep_type_str = "BULK"; + break; + case USB_BM_ATTRIBUTES_XFER_INT: + ep_type_str = "INT"; + break; + default: + ep_type_str = NULL; + break; + } + + ESP_LOGV(TAG, "\t\t*** Endpoint descriptor ***"); + ESP_LOGV(TAG, "\t\tbLength %d", ep_desc->bLength); + ESP_LOGV(TAG, "\t\tbDescriptorType %d", ep_desc->bDescriptorType); + ESP_LOGV(TAG, "\t\tbEndpointAddress 0x%x\tEP %d %s", ep_desc->bEndpointAddress, USB_EP_DESC_GET_EP_NUM(ep_desc), + USB_EP_DESC_GET_EP_DIR(ep_desc) ? "IN" : "OUT"); + ESP_LOGV(TAG, "\t\tbmAttributes 0x%x\t%s", ep_desc->bmAttributes, ep_type_str); + ESP_LOGV(TAG, "\t\twMaxPacketSize %d", ep_desc->wMaxPacketSize); + ESP_LOGV(TAG, "\t\tbInterval %d", ep_desc->bInterval); +} + +static void usbh_print_intf_desc(const usb_intf_desc_t *intf_desc) { + ESP_LOGV(TAG, "\t*** Interface descriptor ***"); + ESP_LOGV(TAG, "\tbLength %d", intf_desc->bLength); + ESP_LOGV(TAG, "\tbDescriptorType %d", intf_desc->bDescriptorType); + ESP_LOGV(TAG, "\tbInterfaceNumber %d", intf_desc->bInterfaceNumber); + ESP_LOGV(TAG, "\tbAlternateSetting %d", intf_desc->bAlternateSetting); + ESP_LOGV(TAG, "\tbNumEndpoints %d", intf_desc->bNumEndpoints); + ESP_LOGV(TAG, "\tbInterfaceClass 0x%x", intf_desc->bInterfaceProtocol); + ESP_LOGV(TAG, "\tiInterface %d", intf_desc->iInterface); +} + +static void usbh_print_cfg_desc(const usb_config_desc_t *cfg_desc) { + ESP_LOGV(TAG, "*** Configuration descriptor ***"); + ESP_LOGV(TAG, "bLength %d", cfg_desc->bLength); + ESP_LOGV(TAG, "bDescriptorType %d", cfg_desc->bDescriptorType); + ESP_LOGV(TAG, "wTotalLength %d", cfg_desc->wTotalLength); + ESP_LOGV(TAG, "bNumInterfaces %d", cfg_desc->bNumInterfaces); + ESP_LOGV(TAG, "bConfigurationValue %d", cfg_desc->bConfigurationValue); + ESP_LOGV(TAG, "iConfiguration %d", cfg_desc->iConfiguration); + ESP_LOGV(TAG, "bmAttributes 0x%x", cfg_desc->bmAttributes); + ESP_LOGV(TAG, "bMaxPower %dmA", cfg_desc->bMaxPower * 2); +} + +void usb_client_print_device_descriptor(const usb_device_desc_t *devc_desc) { + if (devc_desc == NULL) { + return; + } + + ESP_LOGV(TAG, "*** Device descriptor ***"); + ESP_LOGV(TAG, "bLength %d", devc_desc->bLength); + ESP_LOGV(TAG, "bDescriptorType %d", devc_desc->bDescriptorType); + ESP_LOGV(TAG, "bcdUSB %d.%d0", ((devc_desc->bcdUSB >> 8) & 0xF), ((devc_desc->bcdUSB >> 4) & 0xF)); + ESP_LOGV(TAG, "bDeviceClass 0x%x", devc_desc->bDeviceClass); + ESP_LOGV(TAG, "bDeviceSubClass 0x%x", devc_desc->bDeviceSubClass); + ESP_LOGV(TAG, "bDeviceProtocol 0x%x", devc_desc->bDeviceProtocol); + ESP_LOGV(TAG, "bMaxPacketSize0 %d", devc_desc->bMaxPacketSize0); + ESP_LOGV(TAG, "idVendor 0x%x", devc_desc->idVendor); + ESP_LOGV(TAG, "idProduct 0x%x", devc_desc->idProduct); + ESP_LOGV(TAG, "bcdDevice %d.%d0", ((devc_desc->bcdDevice >> 8) & 0xF), ((devc_desc->bcdDevice >> 4) & 0xF)); + ESP_LOGV(TAG, "iManufacturer %d", devc_desc->iManufacturer); + ESP_LOGV(TAG, "iProduct %d", devc_desc->iProduct); + ESP_LOGV(TAG, "iSerialNumber %d", devc_desc->iSerialNumber); + ESP_LOGV(TAG, "bNumConfigurations %d", devc_desc->bNumConfigurations); +} + +void usb_client_print_config_descriptor(const usb_config_desc_t *cfg_desc, + print_class_descriptor_cb class_specific_cb) { + if (cfg_desc == nullptr) { + return; + } + + int offset = 0; + uint16_t w_total_length = cfg_desc->wTotalLength; + const usb_standard_desc_t *next_desc = (const usb_standard_desc_t *) cfg_desc; + + do { + switch (next_desc->bDescriptorType) { + case USB_W_VALUE_DT_CONFIG: + usbh_print_cfg_desc((const usb_config_desc_t *) next_desc); + break; + case USB_W_VALUE_DT_INTERFACE: + usbh_print_intf_desc((const usb_intf_desc_t *) next_desc); + break; + case USB_W_VALUE_DT_ENDPOINT: + print_ep_desc((const usb_ep_desc_t *) next_desc); + break; + default: + if (class_specific_cb) { + class_specific_cb(next_desc); + } + break; + } + + next_desc = usb_parse_next_descriptor(next_desc, w_total_length, &offset); + + } while (next_desc != NULL); +} +#endif +static std::string get_descriptor_string(const usb_str_desc_t *desc) { + char buffer[256]; + if (desc == nullptr) + return "(unknown)"; + char *p = buffer; + for (size_t i = 0; i != desc->bLength / 2; i++) { + auto c = desc->wData[i]; + if (c < 0x100) + *p++ = static_cast(c); + } + *p = '\0'; + return {buffer}; +} + +static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *ptr) { + auto *client = static_cast(ptr); + switch (event_msg->event) { + case USB_HOST_CLIENT_EVENT_NEW_DEV: { + auto addr = event_msg->new_dev.address; + ESP_LOGD(TAG, "New device %d", event_msg->new_dev.address); + client->on_opened(addr); + break; + } + case USB_HOST_CLIENT_EVENT_DEV_GONE: { + client->on_removed(event_msg->dev_gone.dev_hdl); + ESP_LOGD(TAG, "Device gone %d", event_msg->new_dev.address); + break; + } + default: + ESP_LOGD(TAG, "Unknown event %d", event_msg->event); + break; + } +} +void USBClient::setup() { + usb_host_client_config_t config{.is_synchronous = false, + .max_num_event_msg = 5, + .async = {.client_event_callback = client_event_cb, .callback_arg = this}}; + auto err = usb_host_client_register(&config, &this->handle_); + if (err != ESP_OK) { + ESP_LOGE(TAG, "client register failed: %s", esp_err_to_name(err)); + this->status_set_error("Client register failed"); + this->mark_failed(); + return; + } + for (auto trq : this->trq_pool_) { + usb_host_transfer_alloc(64, 0, &trq->transfer); + trq->client = this; + } + ESP_LOGCONFIG(TAG, "client setup complete"); +} + +void USBClient::loop() { + switch (this->state_) { + case USB_CLIENT_OPEN: { + int err; + ESP_LOGD(TAG, "Open device %d", this->device_addr_); + err = usb_host_device_open(this->handle_, this->device_addr_, &this->device_handle_); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Device open failed: %s", esp_err_to_name(err)); + this->state_ = USB_CLIENT_INIT; + break; + } + ESP_LOGD(TAG, "Get descriptor device %d", this->device_addr_); + const usb_device_desc_t *desc; + err = usb_host_get_device_descriptor(this->device_handle_, &desc); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Device get_desc failed: %s", esp_err_to_name(err)); + this->disconnect(); + } else { + ESP_LOGD(TAG, "Device descriptor: vid %X pid %X", desc->idVendor, desc->idProduct); + if (desc->idVendor == this->vid_ && desc->idProduct == this->pid_ || this->vid_ == 0 && this->pid_ == 0) { + usb_device_info_t dev_info; + if ((err = usb_host_device_info(this->device_handle_, &dev_info)) != ESP_OK) { + ESP_LOGW(TAG, "Device info failed: %s", esp_err_to_name(err)); + this->disconnect(); + break; + } + this->state_ = USB_CLIENT_CONNECTED; + ESP_LOGD(TAG, "Device connected: Manuf: %s; Prod: %s; Serial: %s", + get_descriptor_string(dev_info.str_desc_manufacturer).c_str(), + get_descriptor_string(dev_info.str_desc_product).c_str(), + get_descriptor_string(dev_info.str_desc_serial_num).c_str()); + +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE + const usb_device_desc_t *device_desc; + err = usb_host_get_device_descriptor(this->device_handle_, &device_desc); + if (err == ESP_OK) + usb_client_print_device_descriptor(device_desc); + const usb_config_desc_t *config_desc; + err = usb_host_get_active_config_descriptor(this->device_handle_, &config_desc); + if (err == ESP_OK) + usb_client_print_config_descriptor(config_desc, nullptr); +#endif + this->on_connected(); + } else { + ESP_LOGD(TAG, "Not our device, closing"); + this->disconnect(); + } + } + break; + } + + default: + usb_host_client_handle_events(this->handle_, 0); + break; + } +} + +void USBClient::on_opened(uint8_t addr) { + if (this->state_ == USB_CLIENT_INIT) { + this->device_addr_ = addr; + this->state_ = USB_CLIENT_OPEN; + } +} +void USBClient::on_removed(usb_device_handle_t handle) { + if (this->device_handle_ == handle) { + this->disconnect(); + } +} + +static void control_callback(const usb_transfer_t *xfer) { + auto *trq = static_cast(xfer->context); + trq->status.error_code = xfer->status; + trq->status.success = xfer->status == USB_TRANSFER_STATUS_COMPLETED; + trq->status.endpoint = xfer->bEndpointAddress; + trq->status.data = xfer->data_buffer; + trq->status.data_len = xfer->actual_num_bytes; + if (trq->callback != nullptr) + trq->callback(trq->status); + trq->client->release_trq(trq); +} + +TransferRequest *USBClient::get_trq_() { + if (this->trq_pool_.empty()) { + ESP_LOGE(TAG, "Too many requests queued"); + return nullptr; + } + auto *trq = this->trq_pool_.front(); + this->trq_pool_.pop_front(); + trq->client = this; + trq->transfer->context = trq; + trq->transfer->device_handle = this->device_handle_; + return trq; +} +void USBClient::disconnect() { + this->on_disconnected(); + auto err = usb_host_device_close(this->handle_, this->device_handle_); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Device close failed: %s", esp_err_to_name(err)); + } + this->state_ = USB_CLIENT_INIT; + this->device_handle_ = nullptr; + this->device_addr_ = -1; +} + +bool USBClient::control_transfer(uint8_t type, uint8_t request, uint16_t value, uint16_t index, + const transfer_cb_t &callback, const std::vector &data) { + auto *trq = this->get_trq_(); + if (trq == nullptr) + return false; + auto length = data.size(); + if (length > sizeof(trq->transfer->data_buffer_size) - SETUP_PACKET_SIZE) { + ESP_LOGE(TAG, "Control transfer data size too large: %u > %u", length, + sizeof(trq->transfer->data_buffer_size) - sizeof(usb_setup_packet_t)); + this->release_trq(trq); + return false; + } + auto control_packet = ByteBuffer(SETUP_PACKET_SIZE, LITTLE); + control_packet.put_uint8(type); + control_packet.put_uint8(request); + control_packet.put_uint16(value); + control_packet.put_uint16(index); + control_packet.put_uint16(length); + memcpy(trq->transfer->data_buffer, control_packet.get_data().data(), SETUP_PACKET_SIZE); + if (length != 0 && !(type & USB_DIR_IN)) { + memcpy(trq->transfer->data_buffer + SETUP_PACKET_SIZE, data.data(), length); + } + trq->callback = callback; + trq->transfer->bEndpointAddress = type & USB_DIR_MASK; + trq->transfer->num_bytes = static_cast(length + SETUP_PACKET_SIZE); + trq->transfer->callback = reinterpret_cast(control_callback); + auto err = usb_host_transfer_submit_control(this->handle_, trq->transfer); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to submit control transfer, err=%s", esp_err_to_name(err)); + this->release_trq(trq); + return false; + } + return true; +} + +static void transfer_callback(usb_transfer_t *xfer) { + auto *trq = static_cast(xfer->context); + trq->status.error_code = xfer->status; + trq->status.success = xfer->status == USB_TRANSFER_STATUS_COMPLETED; + trq->status.endpoint = xfer->bEndpointAddress; + trq->status.data = xfer->data_buffer; + trq->status.data_len = xfer->actual_num_bytes; + if (trq->callback != nullptr) + trq->callback(trq->status); + trq->client->release_trq(trq); +} +/** + * Performs a transfer input operation. + * + * @param ep_address The endpoint address. + * @param callback The callback function to be called when the transfer is complete. + * @param length The length of the data to be transferred. + * + * @throws None. + */ +void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length) { + auto trq = this->get_trq_(); + if (trq == nullptr) { + ESP_LOGE(TAG, "Too many requests queued"); + return; + } + trq->callback = callback; + trq->transfer->callback = transfer_callback; + trq->transfer->bEndpointAddress = ep_address | USB_DIR_IN; + trq->transfer->num_bytes = length; + auto err = usb_host_transfer_submit(trq->transfer); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to submit transfer, address=%x, length=%d, err=%x", ep_address, length, err); + this->release_trq(trq); + this->disconnect(); + } +} + +/** + * Performs an output transfer operation. + * + * @param ep_address The endpoint address. + * @param callback The callback function to be called when the transfer is complete. + * @param data The data to be transferred. + * @param length The length of the data to be transferred. + * + * @throws None. + */ +void USBClient::transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length) { + auto trq = this->get_trq_(); + if (trq == nullptr) { + ESP_LOGE(TAG, "Too many requests queued"); + return; + } + trq->callback = callback; + trq->transfer->callback = transfer_callback; + trq->transfer->bEndpointAddress = ep_address | USB_DIR_OUT; + trq->transfer->num_bytes = length; + memcpy(trq->transfer->data_buffer, data, length); + auto err = usb_host_transfer_submit(trq->transfer); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to submit transfer, address=%x, length=%d, err=%x", ep_address, length, err); + this->release_trq(trq); + } +} +void USBClient::dump_config() { + ESP_LOGCONFIG(TAG, + "USBClient\n" + " Vendor id %04X\n" + " Product id %04X", + this->vid_, this->pid_); +} +void USBClient::release_trq(TransferRequest *trq) { this->trq_pool_.push_back(trq); } + +} // namespace usb_host +} // namespace esphome +#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 diff --git a/esphome/components/usb_host/usb_host_component.cpp b/esphome/components/usb_host/usb_host_component.cpp new file mode 100644 index 0000000000..63a2ab77cc --- /dev/null +++ b/esphome/components/usb_host/usb_host_component.cpp @@ -0,0 +1,35 @@ +// Should not be needed, but it's required to pass CI clang-tidy checks +#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#include "usb_host.h" +#include +#include "esphome/core/log.h" + +namespace esphome { +namespace usb_host { + +void USBHost::setup() { + ESP_LOGCONFIG(TAG, "Setup starts"); + usb_host_config_t config{}; + + if (usb_host_install(&config) != ESP_OK) { + this->status_set_error("usb_host_install failed"); + this->mark_failed(); + return; + } +} +void USBHost::loop() { + int err; + uint32_t event_flags; + err = usb_host_lib_handle_events(0, &event_flags); + if (err != ESP_OK && err != ESP_ERR_TIMEOUT) { + ESP_LOGD(TAG, "lib_handle_events failed failed: %s", esp_err_to_name(err)); + } + if (event_flags != 0) { + ESP_LOGD(TAG, "Event flags %" PRIu32 "X", event_flags); + } +} + +} // namespace usb_host +} // namespace esphome + +#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 diff --git a/esphome/components/usb_uart/__init__.py b/esphome/components/usb_uart/__init__.py new file mode 100644 index 0000000000..6999b1b955 --- /dev/null +++ b/esphome/components/usb_uart/__init__.py @@ -0,0 +1,134 @@ +import esphome.codegen as cg +from esphome.components.uart import ( + CONF_DATA_BITS, + CONF_PARITY, + CONF_STOP_BITS, + UARTComponent, +) +from esphome.components.usb_host import register_usb_client, usb_device_schema +import esphome.config_validation as cv +from esphome.const import ( + CONF_BAUD_RATE, + CONF_BUFFER_SIZE, + CONF_CHANNELS, + CONF_DEBUG, + CONF_DUMMY_RECEIVER, + CONF_ID, +) +from esphome.cpp_types import Component + +AUTO_LOAD = ["uart", "usb_host", "bytebuffer"] +CODEOWNERS = ["@clydebarrow"] + +usb_uart_ns = cg.esphome_ns.namespace("usb_uart") +USBUartComponent = usb_uart_ns.class_("USBUartComponent", Component) +USBUartChannel = usb_uart_ns.class_("USBUartChannel", UARTComponent) + + +UARTParityOptions = usb_uart_ns.enum("UARTParityOptions") +UART_PARITY_OPTIONS = { + "NONE": UARTParityOptions.UART_CONFIG_PARITY_NONE, + "EVEN": UARTParityOptions.UART_CONFIG_PARITY_EVEN, + "ODD": UARTParityOptions.UART_CONFIG_PARITY_ODD, + "MARK": UARTParityOptions.UART_CONFIG_PARITY_MARK, + "SPACE": UARTParityOptions.UART_CONFIG_PARITY_SPACE, +} + +UARTStopBitsOptions = usb_uart_ns.enum("UARTStopBitsOptions") +UART_STOP_BITS_OPTIONS = { + "1": UARTStopBitsOptions.UART_CONFIG_STOP_BITS_1, + "1.5": UARTStopBitsOptions.UART_CONFIG_STOP_BITS_1_5, + "2": UARTStopBitsOptions.UART_CONFIG_STOP_BITS_2, +} + +DEFAULT_BAUD_RATE = 9600 + + +class Type: + def __init__(self, name, vid, pid, cls, max_channels=1, baud_rate_required=True): + self.name = name + cls = cls or name + self.vid = vid + self.pid = pid + self.cls = usb_uart_ns.class_(f"USBUartType{cls}", USBUartComponent) + self.max_channels = max_channels + self.baud_rate_required = baud_rate_required + + +uart_types = ( + Type("CH34X", 0x1A86, 0x55D5, "CH34X", 3), + Type("CH340", 0x1A86, 0x7523, "CH34X", 1), + Type("ESP_JTAG", 0x303A, 0x1001, "CdcAcm", 1, baud_rate_required=False), + Type("STM32_VCP", 0x0483, 0x5740, "CdcAcm", 1, baud_rate_required=False), + Type("CDC_ACM", 0, 0, "CdcAcm", 1, baud_rate_required=False), + Type("CP210X", 0x10C4, 0xEA60, "CP210X", 3), +) + + +def channel_schema(channels, baud_rate_required): + return cv.Schema( + { + cv.Required(CONF_CHANNELS): cv.All( + cv.ensure_list( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(USBUartChannel), + cv.Optional(CONF_BUFFER_SIZE, default=256): cv.int_range( + min=64, max=8192 + ), + ( + cv.Required(CONF_BAUD_RATE) + if baud_rate_required + else cv.Optional( + CONF_BAUD_RATE, default=DEFAULT_BAUD_RATE + ) + ): cv.int_range(min=300, max=1000000), + cv.Optional(CONF_STOP_BITS, default="1"): cv.enum( + UART_STOP_BITS_OPTIONS, upper=True + ), + cv.Optional(CONF_PARITY, default="NONE"): cv.enum( + UART_PARITY_OPTIONS, upper=True + ), + cv.Optional(CONF_DATA_BITS, default=8): cv.int_range( + min=5, max=8 + ), + cv.Optional(CONF_DUMMY_RECEIVER, default=False): cv.boolean, + cv.Optional(CONF_DEBUG, default=False): cv.boolean, + } + ) + ), + cv.Length(max=channels), + ) + } + ) + + +CONFIG_SCHEMA = cv.ensure_list( + cv.typed_schema( + { + it.name: usb_device_schema(it.cls, it.vid, it.pid).extend( + channel_schema(it.max_channels, it.baud_rate_required) + ) + for it in uart_types + }, + upper=True, + ) +) + + +async def to_code(config): + for device in config: + var = await register_usb_client(device) + for index, channel in enumerate(device[CONF_CHANNELS]): + chvar = cg.new_Pvariable(channel[CONF_ID], index, channel[CONF_BUFFER_SIZE]) + await cg.register_parented(chvar, var) + cg.add(chvar.set_rx_buffer_size(channel[CONF_BUFFER_SIZE])) + cg.add(chvar.set_stop_bits(channel[CONF_STOP_BITS])) + cg.add(chvar.set_data_bits(channel[CONF_DATA_BITS])) + cg.add(chvar.set_parity(channel[CONF_PARITY])) + cg.add(chvar.set_baud_rate(channel[CONF_BAUD_RATE])) + cg.add(chvar.set_dummy_receiver(channel[CONF_DUMMY_RECEIVER])) + cg.add(chvar.set_debug(channel[CONF_DEBUG])) + cg.add(var.add_channel(chvar)) + if channel[CONF_DEBUG]: + cg.add_define("USE_UART_DEBUGGER") diff --git a/esphome/components/usb_uart/ch34x.cpp b/esphome/components/usb_uart/ch34x.cpp new file mode 100644 index 0000000000..74e7933824 --- /dev/null +++ b/esphome/components/usb_uart/ch34x.cpp @@ -0,0 +1,80 @@ +#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#include "usb_uart.h" +#include "usb/usb_host.h" +#include "esphome/core/log.h" + +#include "esphome/components/bytebuffer/bytebuffer.h" + +namespace esphome { +namespace usb_uart { + +using namespace bytebuffer; +/** + * CH34x + */ + +void USBUartTypeCH34X::enable_channels() { + // enable the channels + for (auto channel : this->channels_) { + if (!channel->initialised_) + continue; + usb_host::transfer_cb_t callback = [=](const usb_host::TransferStatus &status) { + if (!status.success) { + ESP_LOGE(TAG, "Control transfer failed, status=%s", esp_err_to_name(status.error_code)); + channel->initialised_ = false; + } + }; + + uint8_t divisor = 7; + uint32_t clk = 12000000; + + auto baud_rate = channel->baud_rate_; + if (baud_rate < 256000) { + if (baud_rate > 6000000 / 255) { + divisor = 3; + clk = 6000000; + } else if (baud_rate > 750000 / 255) { + divisor = 2; + clk = 750000; + } else if (baud_rate > 93750 / 255) { + divisor = 1; + clk = 93750; + } else { + divisor = 0; + clk = 11719; + } + } + ESP_LOGV(TAG, "baud_rate: %" PRIu32 ", divisor: %d, clk: %" PRIu32, baud_rate, divisor, clk); + auto factor = static_cast(clk / baud_rate); + if (factor == 0 || factor == 0xFF) { + ESP_LOGE(TAG, "Invalid baud rate %" PRIu32, baud_rate); + channel->initialised_ = false; + continue; + } + if ((clk / factor - baud_rate) > (baud_rate - clk / (factor + 1))) + factor++; + factor = 256 - factor; + + uint16_t value = 0xC0; + if (channel->stop_bits_ == UART_CONFIG_STOP_BITS_2) + value |= 4; + switch (channel->parity_) { + case UART_CONFIG_PARITY_NONE: + break; + default: + value |= 8 | ((channel->parity_ - 1) << 4); + break; + } + value |= channel->data_bits_ - 5; + value <<= 8; + value |= 0x8C; + uint8_t cmd = 0xA1 + channel->index_; + if (channel->index_ >= 2) + cmd += 0xE; + this->control_transfer(USB_VENDOR_DEV | usb_host::USB_DIR_OUT, cmd, value, (factor << 8) | divisor, callback); + } + USBUartTypeCdcAcm::enable_channels(); +} +} // namespace usb_uart +} // namespace esphome +#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 diff --git a/esphome/components/usb_uart/cp210x.cpp b/esphome/components/usb_uart/cp210x.cpp new file mode 100644 index 0000000000..267385d1bd --- /dev/null +++ b/esphome/components/usb_uart/cp210x.cpp @@ -0,0 +1,126 @@ +#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#include "usb_uart.h" +#include "usb/usb_host.h" +#include "esphome/core/log.h" + +#include "esphome/components/bytebuffer/bytebuffer.h" + +namespace esphome { +namespace usb_uart { + +using namespace bytebuffer; +/** + * Silabs CP210x Commands + */ + +static constexpr uint8_t IFC_ENABLE = 0x00; // Enable or disable the interface. +static constexpr uint8_t SET_BAUDDIV = 0x01; // Set the baud rate divisor. +static constexpr uint8_t GET_BAUDDIV = 0x02; // Get the baud rate divisor. +static constexpr uint8_t SET_LINE_CTL = 0x03; // Set the line control. +static constexpr uint8_t GET_LINE_CTL = 0x04; // Get the line control. +static constexpr uint8_t SET_BREAK = 0x05; // Set a BREAK. +static constexpr uint8_t IMM_CHAR = 0x06; // Send character out of order. +static constexpr uint8_t SET_MHS = 0x07; // Set modem handshaking. +static constexpr uint8_t GET_MDMSTS = 0x08; // Get modem status. +static constexpr uint8_t SET_XON = 0x09; // Emulate XON. +static constexpr uint8_t SET_XOFF = 0x0A; // Emulate XOFF. +static constexpr uint8_t SET_EVENTMASK = 0x0B; // Set the event mask. +static constexpr uint8_t GET_EVENTMASK = 0x0C; // Get the event mask. +static constexpr uint8_t GET_EVENTSTATE = 0x16; // Get the event state. +static constexpr uint8_t SET_RECEIVE = 0x17; // Set receiver max timeout. +static constexpr uint8_t GET_RECEIVE = 0x18; // Get receiver max timeout. +static constexpr uint8_t SET_CHAR = 0x0D; // Set special character individually. +static constexpr uint8_t GET_CHARS = 0x0E; // Get special characters. +static constexpr uint8_t GET_PROPS = 0x0F; // Get properties. +static constexpr uint8_t GET_COMM_STATUS = 0x10; // Get the serial status. +static constexpr uint8_t RESET = 0x11; // Reset. +static constexpr uint8_t PURGE = 0x12; // Purge. +static constexpr uint8_t SET_FLOW = 0x13; // Set flow control. +static constexpr uint8_t GET_FLOW = 0x14; // Get flow control. +static constexpr uint8_t EMBED_EVENTS = 0x15; // Control embedding of events in the data stream. +static constexpr uint8_t GET_BAUDRATE = 0x1D; // Get the baud rate. +static constexpr uint8_t SET_BAUDRATE = 0x1E; // Set the baud rate. +static constexpr uint8_t SET_CHARS = 0x19; // Set special characters. +static constexpr uint8_t VENDOR_SPECIFIC = 0xFF; // Vendor specific command. + +std::vector USBUartTypeCP210X::parse_descriptors_(usb_device_handle_t dev_hdl) { + const usb_config_desc_t *config_desc; + const usb_device_desc_t *device_desc; + int conf_offset = 0, ep_offset; + std::vector cdc_devs{}; + + // Get required descriptors + if (usb_host_get_device_descriptor(dev_hdl, &device_desc) != ESP_OK) { + ESP_LOGE(TAG, "get_device_descriptor failed"); + return {}; + } + if (usb_host_get_active_config_descriptor(dev_hdl, &config_desc) != ESP_OK) { + ESP_LOGE(TAG, "get_active_config_descriptor failed"); + return {}; + } + ESP_LOGD(TAG, "bDeviceClass: %u, bDeviceSubClass: %u", device_desc->bDeviceClass, device_desc->bDeviceSubClass); + ESP_LOGD(TAG, "bNumInterfaces: %u", config_desc->bNumInterfaces); + if (device_desc->bDeviceClass != 0) { + ESP_LOGE(TAG, "bDeviceClass != 0"); + return {}; + } + + for (uint8_t i = 0; i != config_desc->bNumInterfaces; i++) { + auto data_desc = usb_parse_interface_descriptor(config_desc, 0, 0, &conf_offset); + if (!data_desc) { + ESP_LOGE(TAG, "data_desc: usb_parse_interface_descriptor failed"); + break; + } + if (data_desc->bNumEndpoints != 2 || data_desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) { + ESP_LOGE(TAG, "data_desc: bInterfaceClass == %u, bInterfaceSubClass == %u, bNumEndpoints == %u", + data_desc->bInterfaceClass, data_desc->bInterfaceSubClass, data_desc->bNumEndpoints); + continue; + } + ep_offset = conf_offset; + auto out_ep = usb_parse_endpoint_descriptor_by_index(data_desc, 0, config_desc->wTotalLength, &ep_offset); + if (!out_ep) { + ESP_LOGE(TAG, "out_ep: usb_parse_endpoint_descriptor_by_index failed"); + continue; + } + ep_offset = conf_offset; + auto in_ep = usb_parse_endpoint_descriptor_by_index(data_desc, 1, config_desc->wTotalLength, &ep_offset); + if (!in_ep) { + ESP_LOGE(TAG, "in_ep: usb_parse_endpoint_descriptor_by_index failed"); + continue; + } + if (in_ep->bEndpointAddress & usb_host::USB_DIR_IN) { + cdc_devs.push_back({CdcEps{nullptr, in_ep, out_ep, data_desc->bInterfaceNumber}}); + } else { + cdc_devs.push_back({CdcEps{nullptr, out_ep, in_ep, data_desc->bInterfaceNumber}}); + } + } + return cdc_devs; +} + +void USBUartTypeCP210X::enable_channels() { + // enable the channels + for (auto channel : this->channels_) { + if (!channel->initialised_) + continue; + usb_host::transfer_cb_t callback = [=](const usb_host::TransferStatus &status) { + if (!status.success) { + ESP_LOGE(TAG, "Control transfer failed, status=%s", esp_err_to_name(status.error_code)); + channel->initialised_ = false; + } + }; + this->control_transfer(USB_VENDOR_IFC | usb_host::USB_DIR_OUT, IFC_ENABLE, 1, channel->index_, callback); + uint16_t line_control = channel->stop_bits_; + line_control |= static_cast(channel->parity_) << 4; + line_control |= channel->data_bits_ << 8; + ESP_LOGD(TAG, "Line control value 0x%X", line_control); + this->control_transfer(USB_VENDOR_IFC | usb_host::USB_DIR_OUT, SET_LINE_CTL, line_control, channel->index_, + callback); + auto baud = ByteBuffer::wrap(channel->baud_rate_, LITTLE); + this->control_transfer(USB_VENDOR_IFC | usb_host::USB_DIR_OUT, SET_BAUDRATE, 0, channel->index_, callback, + baud.get_data()); + } + USBUartTypeCdcAcm::enable_channels(); +} +} // namespace usb_uart +} // namespace esphome +#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 diff --git a/esphome/components/usb_uart/usb_uart.cpp b/esphome/components/usb_uart/usb_uart.cpp new file mode 100644 index 0000000000..e599409f0c --- /dev/null +++ b/esphome/components/usb_uart/usb_uart.cpp @@ -0,0 +1,328 @@ +// Should not be needed, but it's required to pass CI clang-tidy checks +#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#include "usb_uart.h" +#include "esphome/core/log.h" +#include "esphome/components/uart/uart_debugger.h" + +#include + +namespace esphome { +namespace usb_uart { + +/** + * + * Given a configuration, look for the required interfaces defining a CDC-ACM device + * @param config_desc The configuration descriptor + * @param intf_idx The index of the interface to be examined + * @return + */ +static optional get_cdc(const usb_config_desc_t *config_desc, uint8_t intf_idx) { + int conf_offset, ep_offset; + const usb_ep_desc_t *notify_ep{}, *in_ep{}, *out_ep{}; + uint8_t interface_number = 0; + // look for an interface with one interrupt endpoint (notify), and an interface with two bulk endpoints (data in/out) + for (;;) { + auto intf_desc = usb_parse_interface_descriptor(config_desc, intf_idx++, 0, &conf_offset); + if (!intf_desc) { + ESP_LOGE(TAG, "usb_parse_interface_descriptor failed"); + return nullopt; + } + if (intf_desc->bNumEndpoints == 1) { + ep_offset = conf_offset; + notify_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, config_desc->wTotalLength, &ep_offset); + if (!notify_ep) { + ESP_LOGE(TAG, "notify_ep: usb_parse_endpoint_descriptor_by_index failed"); + return nullopt; + } + if (notify_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_INT) + notify_ep = nullptr; + } else if (USB_CLASS_CDC_DATA && intf_desc->bNumEndpoints == 2) { + interface_number = intf_desc->bInterfaceNumber; + ep_offset = conf_offset; + out_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, config_desc->wTotalLength, &ep_offset); + if (!out_ep) { + ESP_LOGE(TAG, "out_ep: usb_parse_endpoint_descriptor_by_index failed"); + return nullopt; + } + if (out_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_BULK) + out_ep = nullptr; + ep_offset = conf_offset; + in_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 1, config_desc->wTotalLength, &ep_offset); + if (!in_ep) { + ESP_LOGE(TAG, "in_ep: usb_parse_endpoint_descriptor_by_index failed"); + return nullopt; + } + if (in_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_BULK) + in_ep = nullptr; + } + if (in_ep != nullptr && out_ep != nullptr && notify_ep != nullptr) + break; + } + if (in_ep->bEndpointAddress & usb_host::USB_DIR_IN) + return CdcEps{notify_ep, in_ep, out_ep, interface_number}; + return CdcEps{notify_ep, out_ep, in_ep, interface_number}; +} + +std::vector USBUartTypeCdcAcm::parse_descriptors_(usb_device_handle_t dev_hdl) { + const usb_config_desc_t *config_desc; + const usb_device_desc_t *device_desc; + int desc_offset = 0; + std::vector cdc_devs{}; + + // Get required descriptors + if (usb_host_get_device_descriptor(dev_hdl, &device_desc) != ESP_OK) { + ESP_LOGE(TAG, "get_device_descriptor failed"); + return {}; + } + if (usb_host_get_active_config_descriptor(dev_hdl, &config_desc) != ESP_OK) { + ESP_LOGE(TAG, "get_active_config_descriptor failed"); + return {}; + } + if (device_desc->bDeviceClass == USB_CLASS_COMM) { + // single CDC-ACM device + if (auto eps = get_cdc(config_desc, 0)) { + ESP_LOGV(TAG, "Found CDC-ACM device"); + cdc_devs.push_back(*eps); + } + return cdc_devs; + } + if (((device_desc->bDeviceClass == USB_CLASS_MISC) && (device_desc->bDeviceSubClass == USB_SUBCLASS_COMMON) && + (device_desc->bDeviceProtocol == USB_DEVICE_PROTOCOL_IAD)) || + ((device_desc->bDeviceClass == USB_CLASS_PER_INTERFACE) && (device_desc->bDeviceSubClass == USB_SUBCLASS_NULL) && + (device_desc->bDeviceProtocol == USB_PROTOCOL_NULL))) { + // This is a composite device, that uses Interface Association Descriptor + const auto *this_desc = reinterpret_cast(config_desc); + for (;;) { + this_desc = usb_parse_next_descriptor_of_type(this_desc, config_desc->wTotalLength, + USB_B_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, &desc_offset); + if (!this_desc) + break; + const auto *iad_desc = reinterpret_cast(this_desc); + + if (iad_desc->bFunctionClass == USB_CLASS_COMM && iad_desc->bFunctionSubClass == USB_CDC_SUBCLASS_ACM) { + ESP_LOGV(TAG, "Found CDC-ACM device in composite device"); + if (auto eps = get_cdc(config_desc, iad_desc->bFirstInterface)) + cdc_devs.push_back(*eps); + } + } + } + return cdc_devs; +} + +void RingBuffer::push(uint8_t item) { + this->buffer_[this->insert_pos_] = item; + this->insert_pos_ = (this->insert_pos_ + 1) % this->buffer_size_; +} +void RingBuffer::push(const uint8_t *data, size_t len) { + for (size_t i = 0; i != len; i++) { + this->buffer_[this->insert_pos_] = *data++; + this->insert_pos_ = (this->insert_pos_ + 1) % this->buffer_size_; + } +} + +uint8_t RingBuffer::pop() { + uint8_t item = this->buffer_[this->read_pos_]; + this->read_pos_ = (this->read_pos_ + 1) % this->buffer_size_; + return item; +} +size_t RingBuffer::pop(uint8_t *data, size_t len) { + len = std::min(len, this->get_available()); + for (size_t i = 0; i != len; i++) { + *data++ = this->buffer_[this->read_pos_]; + this->read_pos_ = (this->read_pos_ + 1) % this->buffer_size_; + } + return len; +} +void USBUartChannel::write_array(const uint8_t *data, size_t len) { + if (!this->initialised_) { + ESP_LOGV(TAG, "Channel not initialised - write ignored"); + return; + } + while (this->output_buffer_.get_free_space() != 0 && len-- != 0) { + this->output_buffer_.push(*data++); + } + len++; + if (len > 0) { + ESP_LOGE(TAG, "Buffer full - failed to write %d bytes", len); + } + this->parent_->start_output(this); +} + +bool USBUartChannel::peek_byte(uint8_t *data) { + if (this->input_buffer_.is_empty()) { + return false; + } + *data = this->input_buffer_.peek(); + return true; +} +bool USBUartChannel::read_array(uint8_t *data, size_t len) { + if (!this->initialised_) { + ESP_LOGV(TAG, "Channel not initialised - read ignored"); + return false; + } + auto available = this->available(); + bool status = true; + if (len > available) { + ESP_LOGV(TAG, "underflow: requested %zu but returned %d, bytes", len, available); + len = available; + status = false; + } + for (size_t i = 0; i != len; i++) { + *data++ = this->input_buffer_.pop(); + } + this->parent_->start_input(this); + return status; +} +void USBUartComponent::setup() { USBClient::setup(); } +void USBUartComponent::loop() { USBClient::loop(); } +void USBUartComponent::dump_config() { + USBClient::dump_config(); + for (auto &channel : this->channels_) { + ESP_LOGCONFIG(TAG, + " UART Channel %d\n" + " Baud Rate: %" PRIu32 " baud\n" + " Data Bits: %u\n" + " Parity: %s\n" + " Stop bits: %s\n" + " Debug: %s\n" + " Dummy receiver: %s", + channel->index_, channel->baud_rate_, channel->data_bits_, PARITY_NAMES[channel->parity_], + STOP_BITS_NAMES[channel->stop_bits_], YESNO(channel->debug_), YESNO(channel->dummy_receiver_)); + } +} +void USBUartComponent::start_input(USBUartChannel *channel) { + if (!channel->initialised_ || channel->input_started_ || + channel->input_buffer_.get_free_space() < channel->cdc_dev_.in_ep->wMaxPacketSize) + return; + auto ep = channel->cdc_dev_.in_ep; + auto callback = [this, channel](const usb_host::TransferStatus &status) { + ESP_LOGV(TAG, "Transfer result: length: %u; status %X", status.data_len, status.error_code); + if (!status.success) { + ESP_LOGE(TAG, "Control transfer failed, status=%s", esp_err_to_name(status.error_code)); + return; + } +#ifdef USE_UART_DEBUGGER + if (channel->debug_) { + uart::UARTDebug::log_hex(uart::UART_DIRECTION_RX, + std::vector(status.data, status.data + status.data_len), ','); // NOLINT() + } +#endif + channel->input_started_ = false; + if (!channel->dummy_receiver_) { + for (size_t i = 0; i != status.data_len; i++) { + channel->input_buffer_.push(status.data[i]); + } + } + if (channel->input_buffer_.get_free_space() >= channel->cdc_dev_.in_ep->wMaxPacketSize) { + this->defer([this, channel] { this->start_input(channel); }); + } + }; + channel->input_started_ = true; + this->transfer_in(ep->bEndpointAddress, callback, ep->wMaxPacketSize); +} + +void USBUartComponent::start_output(USBUartChannel *channel) { + if (channel->output_started_) + return; + if (channel->output_buffer_.is_empty()) { + return; + } + auto ep = channel->cdc_dev_.out_ep; + auto callback = [this, channel](const usb_host::TransferStatus &status) { + ESP_LOGV(TAG, "Output Transfer result: length: %u; status %X", status.data_len, status.error_code); + channel->output_started_ = false; + this->defer([this, channel] { this->start_output(channel); }); + }; + channel->output_started_ = true; + uint8_t data[ep->wMaxPacketSize]; + auto len = channel->output_buffer_.pop(data, ep->wMaxPacketSize); + this->transfer_out(ep->bEndpointAddress, callback, data, len); +#ifdef USE_UART_DEBUGGER + if (channel->debug_) { + uart::UARTDebug::log_hex(uart::UART_DIRECTION_TX, std::vector(data, data + len), ','); // NOLINT() + } +#endif + ESP_LOGV(TAG, "Output %d bytes started", len); +} + +/** + * Hacky fix for some devices that report incorrect MPS values + * @param ep The endpoint descriptor + */ +static void fix_mps(const usb_ep_desc_t *ep) { + if (ep != nullptr) { + auto *ep_mutable = const_cast(ep); + if (ep->wMaxPacketSize > 64) { + ESP_LOGW(TAG, "Corrected MPS of EP %u from %u to 64", ep->bEndpointAddress, ep->wMaxPacketSize); + ep_mutable->wMaxPacketSize = 64; + } + } +} +void USBUartTypeCdcAcm::on_connected() { + auto cdc_devs = this->parse_descriptors_(this->device_handle_); + if (cdc_devs.empty()) { + this->status_set_error("No CDC-ACM device found"); + this->disconnect(); + return; + } + ESP_LOGD(TAG, "Found %zu CDC-ACM devices", cdc_devs.size()); + auto i = 0; + for (auto channel : this->channels_) { + if (i == cdc_devs.size()) { + ESP_LOGE(TAG, "No configuration found for channel %d", channel->index_); + this->status_set_warning("No configuration found for channel"); + break; + } + channel->cdc_dev_ = cdc_devs[i++]; + fix_mps(channel->cdc_dev_.in_ep); + fix_mps(channel->cdc_dev_.out_ep); + channel->initialised_ = true; + auto err = usb_host_interface_claim(this->handle_, this->device_handle_, channel->cdc_dev_.interface_number, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "usb_host_interface_claim failed: %s, channel=%d, intf=%d", esp_err_to_name(err), channel->index_, + channel->cdc_dev_.interface_number); + this->status_set_error("usb_host_interface_claim failed"); + this->disconnect(); + return; + } + } + this->enable_channels(); +} + +void USBUartTypeCdcAcm::on_disconnected() { + for (auto channel : this->channels_) { + if (channel->cdc_dev_.in_ep != nullptr) { + usb_host_endpoint_halt(this->device_handle_, channel->cdc_dev_.in_ep->bEndpointAddress); + usb_host_endpoint_flush(this->device_handle_, channel->cdc_dev_.in_ep->bEndpointAddress); + } + if (channel->cdc_dev_.out_ep != nullptr) { + usb_host_endpoint_halt(this->device_handle_, channel->cdc_dev_.out_ep->bEndpointAddress); + usb_host_endpoint_flush(this->device_handle_, channel->cdc_dev_.out_ep->bEndpointAddress); + } + if (channel->cdc_dev_.notify_ep != nullptr) { + usb_host_endpoint_halt(this->device_handle_, channel->cdc_dev_.notify_ep->bEndpointAddress); + usb_host_endpoint_flush(this->device_handle_, channel->cdc_dev_.notify_ep->bEndpointAddress); + } + usb_host_interface_release(this->handle_, this->device_handle_, channel->cdc_dev_.interface_number); + channel->initialised_ = false; + channel->input_started_ = false; + channel->output_started_ = false; + channel->input_buffer_.clear(); + channel->output_buffer_.clear(); + } + USBClient::on_disconnected(); +} + +void USBUartTypeCdcAcm::enable_channels() { + for (auto channel : this->channels_) { + if (!channel->initialised_) + continue; + channel->input_started_ = false; + channel->output_started_ = false; + this->start_input(channel); + } +} + +} // namespace usb_uart +} // namespace esphome +#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 diff --git a/esphome/components/usb_uart/usb_uart.h b/esphome/components/usb_uart/usb_uart.h new file mode 100644 index 0000000000..fd0fb2c59a --- /dev/null +++ b/esphome/components/usb_uart/usb_uart.h @@ -0,0 +1,151 @@ +#pragma once + +#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" +#include "esphome/components/uart/uart_component.h" +#include "esphome/components/usb_host/usb_host.h" + +namespace esphome { +namespace usb_uart { +class USBUartTypeCdcAcm; +class USBUartComponent; + +static const char *const TAG = "usb_uart"; + +static constexpr uint8_t USB_CDC_SUBCLASS_ACM = 0x02; +static constexpr uint8_t USB_SUBCLASS_COMMON = 0x02; +static constexpr uint8_t USB_SUBCLASS_NULL = 0x00; +static constexpr uint8_t USB_PROTOCOL_NULL = 0x00; +static constexpr uint8_t USB_DEVICE_PROTOCOL_IAD = 0x01; +static constexpr uint8_t USB_VENDOR_IFC = usb_host::USB_TYPE_VENDOR | usb_host::USB_RECIP_INTERFACE; +static constexpr uint8_t USB_VENDOR_DEV = usb_host::USB_TYPE_VENDOR | usb_host::USB_RECIP_DEVICE; + +struct CdcEps { + const usb_ep_desc_t *notify_ep; + const usb_ep_desc_t *in_ep; + const usb_ep_desc_t *out_ep; + uint8_t interface_number; +}; + +enum UARTParityOptions { + UART_CONFIG_PARITY_NONE = 0, + UART_CONFIG_PARITY_ODD, + UART_CONFIG_PARITY_EVEN, + UART_CONFIG_PARITY_MARK, + UART_CONFIG_PARITY_SPACE, +}; + +enum UARTStopBitsOptions { + UART_CONFIG_STOP_BITS_1 = 0, + UART_CONFIG_STOP_BITS_1_5, + UART_CONFIG_STOP_BITS_2, +}; + +static const char *const PARITY_NAMES[] = {"NONE", "ODD", "EVEN", "MARK", "SPACE"}; +static const char *const STOP_BITS_NAMES[] = {"1", "1.5", "2"}; + +class RingBuffer { + public: + RingBuffer(uint16_t buffer_size) : buffer_size_(buffer_size), buffer_(new uint8_t[buffer_size]) {} + bool is_empty() const { return this->read_pos_ == this->insert_pos_; } + size_t get_available() const { + return (this->insert_pos_ + this->buffer_size_ - this->read_pos_) % this->buffer_size_; + }; + size_t get_free_space() const { return this->buffer_size_ - 1 - this->get_available(); } + uint8_t peek() const { return this->buffer_[this->read_pos_]; } + void push(uint8_t item); + void push(const uint8_t *data, size_t len); + uint8_t pop(); + size_t pop(uint8_t *data, size_t len); + void clear() { this->read_pos_ = this->insert_pos_ = 0; } + + protected: + uint16_t insert_pos_ = 0; + uint16_t read_pos_ = 0; + uint16_t buffer_size_; + uint8_t *buffer_; +}; + +class USBUartChannel : public uart::UARTComponent, public Parented { + friend class USBUartComponent; + friend class USBUartTypeCdcAcm; + friend class USBUartTypeCP210X; + friend class USBUartTypeCH34X; + + public: + USBUartChannel(uint8_t index, uint16_t buffer_size) + : index_(index), input_buffer_(RingBuffer(buffer_size)), output_buffer_(RingBuffer(buffer_size)) {} + void write_array(const uint8_t *data, size_t len) override; + ; + bool peek_byte(uint8_t *data) override; + ; + bool read_array(uint8_t *data, size_t len) override; + int available() override { return static_cast(this->input_buffer_.get_available()); } + void flush() override {} + void check_logger_conflict() override {} + void set_parity(UARTParityOptions parity) { this->parity_ = parity; } + void set_debug(bool debug) { this->debug_ = debug; } + void set_dummy_receiver(bool dummy_receiver) { this->dummy_receiver_ = dummy_receiver; } + + protected: + const uint8_t index_; + RingBuffer input_buffer_; + RingBuffer output_buffer_; + UARTParityOptions parity_{UART_CONFIG_PARITY_NONE}; + bool input_started_{true}; + bool output_started_{true}; + CdcEps cdc_dev_{}; + bool debug_{}; + bool dummy_receiver_{}; + bool initialised_{}; +}; + +class USBUartComponent : public usb_host::USBClient { + public: + USBUartComponent(uint16_t vid, uint16_t pid) : usb_host::USBClient(vid, pid) {} + void setup() override; + void loop() override; + void dump_config() override; + std::vector get_channels() { return this->channels_; } + + void add_channel(USBUartChannel *channel) { this->channels_.push_back(channel); } + + void start_input(USBUartChannel *channel); + void start_output(USBUartChannel *channel); + + protected: + std::vector channels_{}; +}; + +class USBUartTypeCdcAcm : public USBUartComponent { + public: + USBUartTypeCdcAcm(uint16_t vid, uint16_t pid) : USBUartComponent(vid, pid) {} + + protected: + virtual std::vector parse_descriptors_(usb_device_handle_t dev_hdl); + void on_connected() override; + virtual void enable_channels(); + void on_disconnected() override; +}; + +class USBUartTypeCP210X : public USBUartTypeCdcAcm { + public: + USBUartTypeCP210X(uint16_t vid, uint16_t pid) : USBUartTypeCdcAcm(vid, pid) {} + + protected: + std::vector parse_descriptors_(usb_device_handle_t dev_hdl) override; + void enable_channels() override; +}; +class USBUartTypeCH34X : public USBUartTypeCdcAcm { + public: + USBUartTypeCH34X(uint16_t vid, uint16_t pid) : USBUartTypeCdcAcm(vid, pid) {} + + protected: + void enable_channels() override; +}; + +} // namespace usb_uart +} // namespace esphome + +#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 diff --git a/esphome/components/valve/__init__.py b/esphome/components/valve/__init__.py index f3c0353777..a6f1428cd2 100644 --- a/esphome/components/valve/__init__.py +++ b/esphome/components/valve/__init__.py @@ -163,6 +163,7 @@ async def register_valve(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_valve(var)) + CORE.register_platform_component("valve", var) await _setup_valve_core(var, config) diff --git a/esphome/components/veml3235/veml3235.cpp b/esphome/components/veml3235/veml3235.cpp index 2410bfdda2..d5489216b6 100644 --- a/esphome/components/veml3235/veml3235.cpp +++ b/esphome/components/veml3235/veml3235.cpp @@ -10,7 +10,7 @@ static const char *const TAG = "veml3235.sensor"; void VEML3235Sensor::setup() { uint8_t device_id[] = {0, 0}; - ESP_LOGCONFIG(TAG, "Setting up VEML3235 '%s'...", this->name_.c_str()); + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str()); if (!this->refresh_config_reg()) { ESP_LOGE(TAG, "Unable to write configuration"); @@ -212,18 +212,22 @@ void VEML3235Sensor::dump_config() { LOG_SENSOR("", "VEML3235", this); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication failed"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } LOG_UPDATE_INTERVAL(this); ESP_LOGCONFIG(TAG, " Auto-gain enabled: %s", YESNO(this->auto_gain_)); if (this->auto_gain_) { - ESP_LOGCONFIG(TAG, " Auto-gain upper threshold: %f%%", this->auto_gain_threshold_high_ * 100.0); - ESP_LOGCONFIG(TAG, " Auto-gain lower threshold: %f%%", this->auto_gain_threshold_low_ * 100.0); - ESP_LOGCONFIG(TAG, " Values below will be used as initial values only"); + ESP_LOGCONFIG(TAG, + " Auto-gain upper threshold: %f%%\n" + " Auto-gain lower threshold: %f%%\n" + " Values below will be used as initial values only", + this->auto_gain_threshold_high_ * 100.0, this->auto_gain_threshold_low_ * 100.0); } - ESP_LOGCONFIG(TAG, " Digital gain: %uX", digital_gain); - ESP_LOGCONFIG(TAG, " Gain: %uX", gain); - ESP_LOGCONFIG(TAG, " Integration time: %ums", integration_time); + ESP_LOGCONFIG(TAG, + " Digital gain: %uX\n" + " Gain: %uX\n" + " Integration time: %ums", + digital_gain, gain, integration_time); } } // namespace veml3235 diff --git a/esphome/components/veml7700/veml7700.cpp b/esphome/components/veml7700/veml7700.cpp index a8c1411c68..9d87a639a6 100644 --- a/esphome/components/veml7700/veml7700.cpp +++ b/esphome/components/veml7700/veml7700.cpp @@ -78,7 +78,7 @@ static const char *get_gain_str(Gain gain) { } void VEML7700Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up VEML7700/6030..."); + ESP_LOGCONFIG(TAG, "Running setup"); auto err = this->configure_(); if (err != i2c::ERROR_OK) { @@ -93,11 +93,15 @@ void VEML7700Component::dump_config() { LOG_I2C_DEVICE(this); ESP_LOGCONFIG(TAG, " Automatic gain/time: %s", YESNO(this->automatic_mode_enabled_)); if (!this->automatic_mode_enabled_) { - ESP_LOGCONFIG(TAG, " Gain: %s", get_gain_str(this->gain_)); - ESP_LOGCONFIG(TAG, " Integration time: %d ms", get_itime_ms(this->integration_time_)); + ESP_LOGCONFIG(TAG, + " Gain: %s\n" + " Integration time: %d ms", + get_gain_str(this->gain_), get_itime_ms(this->integration_time_)); } - ESP_LOGCONFIG(TAG, " Lux compensation: %s", YESNO(this->lux_compensation_enabled_)); - ESP_LOGCONFIG(TAG, " Glass attenuation factor: %f", this->glass_attenuation_factor_); + ESP_LOGCONFIG(TAG, + " Lux compensation: %s\n" + " Glass attenuation factor: %f", + YESNO(this->lux_compensation_enabled_), this->glass_attenuation_factor_); LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "ALS channel lux", this->ambient_light_sensor_); @@ -109,7 +113,7 @@ void VEML7700Component::dump_config() { LOG_SENSOR(" ", "Actual integration time", this->actual_integration_time_sensor_); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with I2C VEML7700/6030 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 1aafea7d85..a692a7556e 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -204,7 +204,7 @@ void VoiceAssistant::loop() { break; } case State::START_PIPELINE: { - ESP_LOGD(TAG, "Requesting start..."); + ESP_LOGD(TAG, "Requesting start"); uint32_t flags = 0; if (!this->continue_conversation_ && this->use_wake_word_) flags |= api::enums::VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD; @@ -223,7 +223,7 @@ void VoiceAssistant::loop() { msg.wake_word_phrase = this->wake_word_; this->wake_word_ = ""; - if (this->api_client_ == nullptr || !this->api_client_->send_voice_assistant_request(msg)) { + if (this->api_client_ == nullptr || !this->api_client_->send_message(msg)) { ESP_LOGW(TAG, "Could not request start"); this->error_trigger_->trigger("not-connected", "Could not request start"); this->continuous_ = false; @@ -245,7 +245,7 @@ void VoiceAssistant::loop() { if (this->audio_mode_ == AUDIO_MODE_API) { api::VoiceAssistantAudio msg; msg.data.assign((char *) this->send_buffer_, read_bytes); - this->api_client_->send_voice_assistant_audio(msg); + this->api_client_->send_message(msg); } else { if (!this->udp_socket_running_) { if (!this->start_udp_socket_()) { @@ -331,7 +331,7 @@ void VoiceAssistant::loop() { api::VoiceAssistantAnnounceFinished msg; msg.success = true; - this->api_client_->send_voice_assistant_announce_finished(msg); + this->api_client_->send_message(msg); break; } } @@ -577,10 +577,10 @@ void VoiceAssistant::signal_stop_() { if (this->api_client_ == nullptr) { return; } - ESP_LOGD(TAG, "Signaling stop..."); + ESP_LOGD(TAG, "Signaling stop"); api::VoiceAssistantRequest msg; msg.start = false; - this->api_client_->send_voice_assistant_request(msg); + this->api_client_->send_message(msg); } void VoiceAssistant::start_playback_timeout_() { @@ -590,7 +590,7 @@ void VoiceAssistant::start_playback_timeout_() { api::VoiceAssistantAnnounceFinished msg; msg.success = true; - this->api_client_->send_voice_assistant_announce_finished(msg); + this->api_client_->send_message(msg); }); } diff --git a/esphome/components/wake_on_lan/wake_on_lan.cpp b/esphome/components/wake_on_lan/wake_on_lan.cpp index d18cdf89c8..bed098755a 100644 --- a/esphome/components/wake_on_lan/wake_on_lan.cpp +++ b/esphome/components/wake_on_lan/wake_on_lan.cpp @@ -30,7 +30,7 @@ void WakeOnLanButton::press_action() { ESP_LOGW(TAG, "Network not connected"); return; } - ESP_LOGI(TAG, "Sending Wake-on-LAN Packet..."); + ESP_LOGI(TAG, "Sending Wake-on-LAN Packet"); #if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) struct sockaddr_storage saddr {}; auto addr_len = diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 79aae70e41..084747c09e 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -1,7 +1,7 @@ #include "waveshare_epaper.h" -#include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include #include diff --git a/esphome/components/web_server/server_index_v2.h b/esphome/components/web_server/server_index_v2.h index 21a2a97465..ec093d3186 100644 --- a/esphome/components/web_server/server_index_v2.h +++ b/esphome/components/web_server/server_index_v2.h @@ -11,636 +11,638 @@ namespace web_server { const uint8_t INDEX_GZ[] PROGMEM = { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xcd, 0x7d, 0xdb, 0x72, 0xdb, 0xc6, 0xb6, 0xe0, 0xf3, - 0xe4, 0x2b, 0x20, 0x44, 0x5b, 0x41, 0x6f, 0x36, 0x21, 0x92, 0x92, 0x6c, 0x19, 0x64, 0x93, 0x5b, 0x96, 0x9d, 0xed, - 0xec, 0xf8, 0x92, 0x58, 0x76, 0xb2, 0x13, 0x86, 0x5b, 0x82, 0x88, 0x26, 0xd9, 0x36, 0x88, 0x66, 0x80, 0x26, 0x25, - 0x85, 0xc4, 0xa9, 0xf9, 0x80, 0xa9, 0x9a, 0xaa, 0x79, 0x9a, 0x97, 0xa9, 0x39, 0x0f, 0xf3, 0x11, 0xf3, 0x7c, 0x3e, - 0xe5, 0xfc, 0xc0, 0xcc, 0x27, 0x4c, 0xad, 0xbe, 0x00, 0x0d, 0x5e, 0x64, 0xe5, 0x72, 0xce, 0x99, 0x4a, 0xc5, 0x22, - 0xfa, 0xba, 0x7a, 0xf5, 0xea, 0x75, 0x6f, 0xa0, 0xb3, 0x17, 0xf1, 0xa1, 0xb8, 0x9b, 0x51, 0x67, 0x22, 0xa6, 0x71, - 0xb7, 0xa3, 0xff, 0xa5, 0x61, 0xd4, 0xed, 0xc4, 0x2c, 0xf9, 0xe8, 0xa4, 0x34, 0x26, 0x6c, 0xc8, 0x13, 0x67, 0x92, - 0xd2, 0x11, 0x89, 0x42, 0x11, 0x06, 0x6c, 0x1a, 0x8e, 0xa9, 0x73, 0xd8, 0xed, 0x4c, 0xa9, 0x08, 0x9d, 0xe1, 0x24, - 0x4c, 0x33, 0x2a, 0xc8, 0xfb, 0x77, 0x5f, 0xd6, 0x4f, 0xbb, 0x9d, 0x6c, 0x98, 0xb2, 0x99, 0x70, 0x60, 0x48, 0x32, - 0xe5, 0xd1, 0x3c, 0xa6, 0xdd, 0xc3, 0xc3, 0x9b, 0x9b, 0x1b, 0xff, 0x43, 0xf6, 0xd9, 0x90, 0x27, 0x99, 0x70, 0x5e, - 0x92, 0x1b, 0x96, 0x44, 0xfc, 0x06, 0x53, 0x41, 0x5e, 0xfa, 0x17, 0x93, 0x30, 0xe2, 0x37, 0x6f, 0x39, 0x17, 0x07, - 0x07, 0x9e, 0x7a, 0xbc, 0x3b, 0xbf, 0xb8, 0x20, 0x84, 0x2c, 0x38, 0x8b, 0x9c, 0xc6, 0x6a, 0x55, 0x16, 0xfa, 0x49, - 0x28, 0xd8, 0x82, 0xaa, 0x2e, 0xe8, 0xe0, 0xc0, 0x0d, 0x23, 0x3e, 0x13, 0x34, 0xba, 0x10, 0x77, 0x31, 0xbd, 0x98, - 0x50, 0x2a, 0x32, 0x97, 0x25, 0xce, 0x33, 0x3e, 0x9c, 0x4f, 0x69, 0x22, 0xfc, 0x59, 0xca, 0x05, 0x07, 0x48, 0x0e, - 0x0e, 0xdc, 0x94, 0xce, 0xe2, 0x70, 0x48, 0xa1, 0xfe, 0xfc, 0xe2, 0xa2, 0xec, 0x51, 0x36, 0xc2, 0x4c, 0x90, 0x8b, - 0xbb, 0xe9, 0x35, 0x8f, 0x3d, 0x84, 0x63, 0x41, 0x12, 0x7a, 0xe3, 0x7c, 0x4f, 0xc3, 0x8f, 0xaf, 0xc2, 0x59, 0x7b, - 0x18, 0x87, 0x59, 0xe6, 0x5c, 0x8b, 0xa5, 0x5c, 0x42, 0x3a, 0x1f, 0x0a, 0x9e, 0x7a, 0x02, 0x53, 0xcc, 0xd0, 0x92, - 0x8d, 0x3c, 0x31, 0x61, 0x99, 0x7f, 0xb9, 0x3f, 0xcc, 0xb2, 0xb7, 0x34, 0x9b, 0xc7, 0x62, 0x9f, 0xec, 0x35, 0x30, - 0xdb, 0x23, 0x84, 0x09, 0x24, 0x26, 0x29, 0xbf, 0x71, 0x9e, 0xa7, 0x29, 0x4f, 0x3d, 0xf7, 0xfc, 0xe2, 0x42, 0xb5, - 0x70, 0x58, 0xe6, 0x24, 0x5c, 0x38, 0xc5, 0x78, 0xe1, 0x75, 0x4c, 0x7d, 0xe7, 0x7d, 0x46, 0x9d, 0xab, 0x79, 0x92, - 0x85, 0x23, 0x7a, 0x7e, 0x71, 0x71, 0xe5, 0xf0, 0xd4, 0xb9, 0x1a, 0x66, 0xd9, 0x95, 0xc3, 0x92, 0x4c, 0xd0, 0x30, - 0xf2, 0x5d, 0xd4, 0x96, 0x93, 0x0d, 0xb3, 0xec, 0x1d, 0xbd, 0x15, 0x44, 0x60, 0xf9, 0x28, 0x08, 0xcd, 0xc7, 0x54, - 0x38, 0x59, 0xb1, 0x2e, 0x0f, 0x2d, 0x63, 0x2a, 0x1c, 0x41, 0x64, 0x3d, 0x6f, 0x2b, 0xdc, 0x53, 0xf5, 0x28, 0xda, - 0x6c, 0xe4, 0x51, 0x71, 0x70, 0x20, 0x0a, 0x3c, 0x23, 0xb5, 0x34, 0x87, 0x11, 0xba, 0x67, 0xca, 0x0e, 0x0e, 0xa8, - 0x1f, 0xd3, 0x64, 0x2c, 0x26, 0x84, 0x90, 0x66, 0x9b, 0x1d, 0x1c, 0x78, 0x82, 0xc4, 0xc2, 0x1f, 0x53, 0xe1, 0x51, - 0x84, 0x70, 0xd9, 0xfb, 0xe0, 0xc0, 0x53, 0x48, 0xe0, 0x44, 0x21, 0xae, 0x82, 0x63, 0xe4, 0x6b, 0xec, 0x5f, 0xdc, - 0x25, 0x43, 0xcf, 0x86, 0x1f, 0x61, 0x76, 0x70, 0x10, 0x0b, 0x3f, 0x83, 0x11, 0xb1, 0x40, 0x28, 0x4f, 0xa9, 0x98, - 0xa7, 0x89, 0x23, 0x72, 0xc1, 0x2f, 0x44, 0xca, 0x92, 0xb1, 0x87, 0x96, 0xa6, 0xcc, 0xea, 0x98, 0xe7, 0x0a, 0xdc, - 0x6f, 0x04, 0x49, 0x49, 0x17, 0x66, 0xbc, 0x16, 0x1e, 0xec, 0x22, 0x1f, 0x39, 0x29, 0x21, 0x6e, 0x26, 0xfb, 0xba, - 0xbd, 0x34, 0x48, 0x6b, 0xae, 0x8b, 0x15, 0x94, 0x98, 0x09, 0x84, 0x3f, 0x12, 0x2f, 0xc5, 0xbe, 0xef, 0x0b, 0x44, - 0xba, 0x4b, 0x83, 0x95, 0xd4, 0x5a, 0x67, 0x2f, 0xed, 0x37, 0x06, 0x81, 0xf0, 0x53, 0x1a, 0xcd, 0x87, 0xd4, 0xf3, - 0x18, 0xce, 0x70, 0x82, 0x48, 0x97, 0xd5, 0x3c, 0x4e, 0xba, 0xb0, 0xdd, 0xbc, 0xba, 0xd7, 0x84, 0xec, 0x35, 0x90, - 0x86, 0x91, 0x1b, 0x00, 0x01, 0xc3, 0x1a, 0x1e, 0x4e, 0x88, 0x9b, 0xcc, 0xa7, 0xd7, 0x34, 0x75, 0x8b, 0x66, 0xed, - 0x0a, 0x59, 0xcc, 0x33, 0xea, 0x0c, 0xb3, 0xcc, 0x19, 0xcd, 0x93, 0xa1, 0x60, 0x3c, 0x71, 0xdc, 0x1a, 0xaf, 0xb9, - 0x8a, 0x1c, 0x0a, 0x6a, 0x70, 0x51, 0x8e, 0xbc, 0x0c, 0xd5, 0xd2, 0x7e, 0x52, 0x6b, 0x0e, 0x30, 0x40, 0x89, 0xda, - 0x7a, 0x3c, 0x8d, 0x00, 0x8a, 0x53, 0x58, 0x63, 0x8e, 0xdf, 0x0b, 0x58, 0xa5, 0x5c, 0x22, 0x15, 0xbd, 0xd4, 0xdf, - 0x3c, 0x28, 0x44, 0xf8, 0xd3, 0x70, 0xe6, 0x51, 0xd2, 0xa5, 0x92, 0xb8, 0xc2, 0x64, 0x08, 0xb0, 0x56, 0xf6, 0xad, - 0x47, 0x03, 0xea, 0x97, 0x24, 0x85, 0x02, 0xe1, 0x8f, 0x78, 0xfa, 0x3c, 0x1c, 0x4e, 0xa0, 0x5f, 0x41, 0x30, 0x91, - 0x39, 0x6f, 0xc3, 0x94, 0x86, 0x82, 0x3e, 0x8f, 0x29, 0x3c, 0x79, 0xae, 0xec, 0xe9, 0x22, 0x9c, 0x91, 0x97, 0x7e, - 0xcc, 0xc4, 0x6b, 0x9e, 0x0c, 0x69, 0x3b, 0xb3, 0xa8, 0x8b, 0xc1, 0xbe, 0x9f, 0x09, 0x91, 0xb2, 0xeb, 0xb9, 0xa0, - 0x9e, 0x9b, 0x40, 0x0b, 0x17, 0x67, 0x08, 0x33, 0x5f, 0xd0, 0x5b, 0x71, 0xce, 0x13, 0x41, 0x13, 0x41, 0xa8, 0x41, - 0x2a, 0x4e, 0xfd, 0x70, 0x36, 0xa3, 0x49, 0x74, 0x3e, 0x61, 0x71, 0xe4, 0x31, 0x94, 0xa3, 0x1c, 0x87, 0x82, 0xc0, - 0x1a, 0x49, 0x37, 0x0d, 0xe0, 0x9f, 0xdd, 0xab, 0xf1, 0x04, 0xe9, 0xca, 0x43, 0x41, 0x89, 0xeb, 0xb6, 0x47, 0x3c, - 0xf5, 0xf4, 0x0a, 0x1c, 0x3e, 0x72, 0x04, 0xcc, 0xf1, 0x76, 0x1e, 0xd3, 0x0c, 0xd1, 0x1a, 0x61, 0xc5, 0x36, 0x6a, - 0x04, 0x7f, 0x03, 0x14, 0x9f, 0x23, 0x2f, 0x45, 0x41, 0xda, 0x5e, 0x84, 0xa9, 0xf3, 0xa5, 0x3e, 0x51, 0xcf, 0x0c, - 0x37, 0x9b, 0x08, 0xf2, 0xcc, 0x17, 0xe9, 0x3c, 0x13, 0x34, 0x7a, 0x77, 0x37, 0xa3, 0x19, 0x7e, 0x27, 0xc8, 0x44, - 0xf4, 0x26, 0xc2, 0xa7, 0xd3, 0x99, 0xb8, 0xbb, 0x90, 0x8c, 0x31, 0x70, 0x5d, 0x3c, 0x84, 0x96, 0x29, 0x0d, 0x87, - 0xc0, 0xcc, 0x34, 0xb6, 0xbe, 0xe1, 0xf1, 0xdd, 0x88, 0xc5, 0xf1, 0xc5, 0x7c, 0x36, 0xe3, 0xa9, 0xc0, 0x7f, 0x25, - 0x4b, 0xc1, 0x4b, 0xd4, 0xc0, 0x5e, 0x2e, 0xb3, 0x1b, 0x26, 0x86, 0x13, 0x4f, 0xa0, 0xe5, 0x30, 0xcc, 0xa8, 0xf3, - 0x94, 0xf3, 0x98, 0x86, 0x49, 0x90, 0x92, 0xb4, 0xf7, 0x4e, 0x04, 0xc9, 0x3c, 0x8e, 0xdb, 0xd7, 0x29, 0x0d, 0x3f, - 0xb6, 0x65, 0xf5, 0x9b, 0xeb, 0x0f, 0x74, 0x28, 0x02, 0xf9, 0xfb, 0x2c, 0x4d, 0xc3, 0x3b, 0x68, 0x48, 0x08, 0x34, - 0xeb, 0xa5, 0xc1, 0xdf, 0x2e, 0xde, 0xbc, 0xf6, 0xd5, 0x21, 0x61, 0xa3, 0x3b, 0x2f, 0x2d, 0x0e, 0x5e, 0x9a, 0xe3, - 0x51, 0xca, 0xa7, 0x6b, 0x53, 0x2b, 0xac, 0xa5, 0xed, 0x1d, 0x20, 0x50, 0x92, 0xee, 0xa9, 0xa1, 0x6d, 0x08, 0x5e, - 0x4b, 0x9a, 0x87, 0x4a, 0xa2, 0xe7, 0x85, 0x7f, 0x02, 0x55, 0xec, 0xa5, 0xe8, 0x7e, 0x68, 0x45, 0x7a, 0xb7, 0xa4, - 0x44, 0xc2, 0x39, 0x03, 0x09, 0x03, 0x30, 0x0e, 0x43, 0x31, 0x9c, 0x2c, 0xa9, 0x1c, 0x2c, 0x37, 0x10, 0xd3, 0x3c, - 0xc7, 0x37, 0x05, 0xbd, 0x8b, 0x3d, 0x42, 0x52, 0xc9, 0xa8, 0x88, 0x58, 0xad, 0x52, 0x42, 0x52, 0x84, 0xbf, 0x27, - 0xcb, 0xd0, 0xac, 0x27, 0xd8, 0x6b, 0x60, 0x38, 0x97, 0x81, 0xe2, 0x2e, 0x78, 0xc8, 0x93, 0x05, 0x4d, 0x05, 0x4d, - 0x83, 0xbf, 0xe2, 0x94, 0x8e, 0x62, 0x80, 0x62, 0xaf, 0x89, 0x27, 0x61, 0x76, 0x3e, 0x09, 0x93, 0x31, 0x8d, 0x82, - 0x1b, 0x91, 0xe3, 0xbf, 0x13, 0x77, 0xc4, 0x92, 0x30, 0x66, 0xbf, 0xd0, 0xc8, 0xd5, 0xd2, 0xe0, 0xcc, 0xa1, 0xb7, - 0x82, 0x26, 0x51, 0xe6, 0xbc, 0x78, 0xf7, 0xea, 0xa5, 0xde, 0xc7, 0x8a, 0x80, 0x40, 0xcb, 0x6c, 0x3e, 0xa3, 0xa9, - 0x87, 0xb0, 0x16, 0x10, 0xcf, 0x99, 0x64, 0x8e, 0xaf, 0xc2, 0x99, 0x2a, 0x61, 0xd9, 0xfb, 0x59, 0x14, 0x0a, 0xfa, - 0x0d, 0x4d, 0x22, 0x96, 0x8c, 0xc9, 0x5e, 0x53, 0x95, 0x4f, 0x42, 0x5d, 0x11, 0x15, 0x45, 0x97, 0xfb, 0xcf, 0x63, - 0xb9, 0xee, 0xe2, 0x71, 0xee, 0xa1, 0x3c, 0x13, 0xa1, 0x60, 0x43, 0x27, 0x8c, 0xa2, 0xaf, 0x12, 0x26, 0x98, 0x04, - 0x30, 0x85, 0xed, 0x01, 0x12, 0xa5, 0x4a, 0x54, 0x18, 0xc0, 0x3d, 0x84, 0x3d, 0x4f, 0x0b, 0x80, 0x09, 0xd2, 0xfb, - 0x75, 0x70, 0x50, 0xb2, 0xfb, 0x1e, 0x0d, 0x54, 0x25, 0xe9, 0x0f, 0x90, 0x3f, 0x9b, 0x67, 0xb0, 0xd1, 0x66, 0x0a, - 0x90, 0x2e, 0xfc, 0x3a, 0xa3, 0xe9, 0x82, 0x46, 0x05, 0x71, 0x64, 0x1e, 0x5a, 0xae, 0xcd, 0xa1, 0x8f, 0x85, 0x20, - 0xfd, 0x41, 0xdb, 0xe6, 0xdb, 0x54, 0xd3, 0x79, 0xca, 0x67, 0x34, 0x15, 0x8c, 0x66, 0x05, 0x2b, 0xf1, 0x40, 0x8a, - 0x16, 0xec, 0x24, 0x23, 0x66, 0x7d, 0x33, 0x8f, 0x61, 0x8a, 0x2a, 0x0c, 0xc3, 0x08, 0xda, 0xe7, 0x0b, 0x29, 0x31, - 0x32, 0xcc, 0x10, 0x16, 0x0a, 0xd2, 0x0c, 0xa1, 0x1c, 0x61, 0x61, 0xc0, 0x55, 0xac, 0x48, 0xcf, 0x76, 0x07, 0xa2, - 0x9a, 0x7c, 0x2f, 0x45, 0x35, 0x30, 0xb4, 0x50, 0xd0, 0x83, 0x03, 0x8f, 0xfa, 0x05, 0x51, 0x90, 0xbd, 0xa6, 0xde, - 0x23, 0x0b, 0x59, 0x3b, 0xc0, 0x86, 0x89, 0x05, 0xa6, 0x08, 0xef, 0x51, 0x3f, 0xe1, 0x67, 0xc3, 0x21, 0xcd, 0x32, - 0x9e, 0x1e, 0x1c, 0xec, 0xc9, 0xf6, 0x85, 0x36, 0x01, 0x7b, 0xf8, 0xe6, 0x26, 0x29, 0x21, 0x40, 0xa5, 0x84, 0xd5, - 0x72, 0x41, 0x80, 0x9c, 0x92, 0x0a, 0x87, 0xdb, 0x33, 0x8a, 0x47, 0xe0, 0x5e, 0x5e, 0xba, 0x35, 0x81, 0x35, 0x1a, - 0xc6, 0xd4, 0x4c, 0x7d, 0xf7, 0x8c, 0x2a, 0xd5, 0x4a, 0x2a, 0x1e, 0x1b, 0x98, 0x51, 0xe7, 0xc7, 0x8f, 0xe8, 0x88, - 0x25, 0xd6, 0xb2, 0x2b, 0x20, 0x61, 0x81, 0x33, 0x94, 0x5b, 0x1b, 0xba, 0x75, 0x68, 0xa9, 0xd3, 0xa8, 0x9d, 0x5b, - 0x8e, 0xa5, 0x1e, 0x61, 0x6d, 0x63, 0x9f, 0x0e, 0x72, 0x2c, 0x51, 0x6f, 0x56, 0x93, 0x48, 0x40, 0xfb, 0x62, 0xd0, - 0xd6, 0xf5, 0x24, 0x53, 0x98, 0x4b, 0xe9, 0xcf, 0x73, 0x9a, 0x09, 0x45, 0xc7, 0x9e, 0xc0, 0x09, 0x66, 0x28, 0x87, - 0xe3, 0x36, 0x62, 0xe3, 0x79, 0x0a, 0xea, 0x0e, 0x1c, 0x45, 0x9a, 0xcc, 0xa7, 0xd4, 0x3c, 0x6d, 0x83, 0xed, 0xcd, - 0x0c, 0x04, 0x62, 0x06, 0x34, 0x7d, 0x3f, 0x39, 0x01, 0xac, 0x02, 0xad, 0x56, 0xdf, 0x9b, 0x41, 0xca, 0xad, 0x2c, - 0x54, 0xb4, 0xb5, 0x3d, 0xf9, 0x3b, 0xd2, 0xf2, 0x78, 0xaf, 0xa9, 0xa0, 0xff, 0xfb, 0x80, 0xec, 0x35, 0x0a, 0x0a, - 0xd6, 0x38, 0x55, 0xc0, 0x28, 0x14, 0xbe, 0x51, 0x03, 0x21, 0x29, 0xdd, 0x2b, 0xc4, 0xe2, 0x4f, 0x36, 0xe8, 0x74, - 0x42, 0xfa, 0xa0, 0x67, 0xf8, 0x93, 0xc1, 0x2e, 0x62, 0x32, 0xdc, 0xc0, 0x13, 0x9b, 0x75, 0x25, 0xd3, 0x58, 0x54, - 0x99, 0xc6, 0xda, 0x22, 0xdc, 0x59, 0xd1, 0xc5, 0x2d, 0x68, 0x4c, 0x1f, 0xf3, 0xb2, 0x0a, 0x33, 0x09, 0x4c, 0xb9, - 0x24, 0x6b, 0x88, 0xd7, 0xe1, 0x94, 0x66, 0x1e, 0x45, 0x78, 0x57, 0x03, 0x45, 0x9c, 0xd0, 0x64, 0x60, 0x89, 0xcd, - 0x0c, 0xc4, 0x26, 0x43, 0x4a, 0x2b, 0xab, 0x1e, 0xb7, 0x0c, 0xd3, 0x7e, 0x36, 0x28, 0x95, 0x39, 0x6b, 0xf1, 0x52, - 0x1e, 0x6b, 0xea, 0x36, 0xf8, 0x53, 0x65, 0x0a, 0x69, 0x52, 0x69, 0xc8, 0x10, 0xde, 0x6b, 0xac, 0xef, 0xa3, 0x69, - 0x55, 0xae, 0xb1, 0x3f, 0x80, 0x7d, 0x90, 0xe2, 0xc2, 0x67, 0x99, 0xfc, 0x5b, 0x39, 0x67, 0x80, 0xb6, 0x0b, 0x20, - 0x0b, 0x7f, 0x14, 0x87, 0xc2, 0x6b, 0x1e, 0x36, 0x40, 0x13, 0x5d, 0x50, 0x90, 0x26, 0x08, 0x6d, 0x2e, 0x85, 0xfa, - 0xf3, 0x24, 0x9b, 0xb0, 0x91, 0xf0, 0x42, 0x21, 0x19, 0x0a, 0x8d, 0x33, 0xea, 0x88, 0x8a, 0x3e, 0x2c, 0x99, 0x4d, - 0x08, 0xa4, 0x56, 0x28, 0x5f, 0xd4, 0x40, 0x2a, 0x99, 0x16, 0xf0, 0x86, 0x52, 0x97, 0x2e, 0x79, 0x8c, 0x69, 0xcd, - 0x40, 0x5f, 0x6c, 0xf6, 0xd4, 0x88, 0x81, 0x66, 0x05, 0xcc, 0x52, 0x59, 0x59, 0x60, 0xf3, 0x07, 0x5d, 0x28, 0x7c, - 0xc1, 0x5f, 0xf2, 0x1b, 0x9a, 0x9e, 0x87, 0x00, 0x7c, 0xa0, 0xba, 0xe7, 0x4a, 0x0c, 0x48, 0x6e, 0x2f, 0xda, 0x86, - 0x5e, 0x2e, 0xe5, 0xc2, 0xbf, 0x49, 0xf9, 0x94, 0x65, 0x14, 0x34, 0x35, 0x85, 0xff, 0x04, 0x4e, 0x99, 0x3c, 0x8e, - 0x20, 0x6a, 0x68, 0x41, 0x5f, 0x67, 0x2f, 0xab, 0xf4, 0x75, 0xb9, 0xff, 0x7c, 0x6c, 0xd8, 0x5f, 0xf5, 0x10, 0x23, - 0xec, 0x69, 0x7b, 0xc2, 0x92, 0x72, 0xfe, 0x04, 0x69, 0xf1, 0xbe, 0x5a, 0x09, 0xcb, 0x6c, 0xab, 0xe8, 0x8a, 0x54, - 0x1d, 0x1b, 0x94, 0x87, 0x51, 0x04, 0x5a, 0x5d, 0xca, 0xe3, 0xd8, 0x12, 0x54, 0x98, 0xb5, 0x0b, 0xd1, 0x74, 0xb9, - 0xff, 0xfc, 0xe2, 0x3e, 0xe9, 0x04, 0xf5, 0xb6, 0x80, 0x32, 0x80, 0x26, 0x11, 0x4d, 0xc1, 0x8c, 0xb4, 0x76, 0x4b, - 0xcb, 0xd8, 0x73, 0x9e, 0x24, 0x74, 0x28, 0x68, 0x04, 0x56, 0x0a, 0x23, 0xc2, 0x9f, 0xf0, 0x4c, 0x14, 0x85, 0x25, - 0xf4, 0xcc, 0x82, 0x9e, 0xf9, 0xc3, 0x30, 0x8e, 0x3d, 0x65, 0x91, 0x4c, 0xf9, 0x82, 0x6e, 0x81, 0xba, 0x5d, 0x01, - 0xb9, 0x18, 0x86, 0x5a, 0xc3, 0x50, 0x3f, 0x9b, 0xc5, 0x6c, 0x48, 0x0b, 0xc1, 0x75, 0xe1, 0xb3, 0x24, 0xa2, 0xb7, - 0xc0, 0x47, 0x50, 0xb7, 0xdb, 0x6d, 0xe0, 0x26, 0xca, 0x15, 0xc2, 0x97, 0x1b, 0x88, 0xbd, 0x47, 0x64, 0x02, 0x91, - 0x91, 0xee, 0x72, 0x1b, 0x3f, 0xa0, 0xc8, 0x92, 0x93, 0xcc, 0x58, 0x56, 0x8a, 0x37, 0x23, 0x1c, 0xd1, 0x98, 0x0a, - 0x6a, 0x78, 0x39, 0xe8, 0xcf, 0xea, 0xe8, 0xbe, 0x2d, 0xf0, 0x57, 0x90, 0x93, 0x39, 0x65, 0x66, 0xcf, 0xb3, 0xc2, - 0x52, 0x2f, 0xb7, 0xa7, 0xc4, 0x76, 0x4f, 0xa8, 0xed, 0x09, 0x85, 0x08, 0x87, 0x13, 0x65, 0xa2, 0x7b, 0x1b, 0x4b, - 0x2a, 0xc7, 0xd0, 0x7c, 0xbd, 0x38, 0x44, 0xef, 0x0d, 0x98, 0xdb, 0x50, 0x70, 0xa1, 0x99, 0x02, 0x05, 0xab, 0x4f, - 0x6d, 0xdb, 0x79, 0x18, 0xc7, 0xd7, 0xe1, 0xf0, 0x63, 0x95, 0xfa, 0x4b, 0x32, 0x20, 0xeb, 0xdc, 0xd8, 0xaa, 0xb2, - 0x58, 0x96, 0xbd, 0x6e, 0xc3, 0xa5, 0x2b, 0x07, 0xc5, 0xdb, 0x6b, 0x94, 0x64, 0x5f, 0xdd, 0xe8, 0x9d, 0xd4, 0x2e, - 0x21, 0x62, 0x7a, 0x65, 0x1e, 0x70, 0x81, 0x4f, 0x52, 0x9c, 0xe1, 0x07, 0x9a, 0xee, 0xc0, 0xd6, 0xc8, 0xd7, 0x00, - 0x11, 0x68, 0x99, 0x47, 0x2c, 0xdb, 0x8d, 0x81, 0x3f, 0x04, 0xca, 0x67, 0xd6, 0x0c, 0x0f, 0x05, 0xb4, 0xe0, 0x71, - 0x5a, 0x65, 0x2e, 0x20, 0xd3, 0xda, 0x84, 0x61, 0x34, 0x5f, 0x83, 0xe6, 0x22, 0xe9, 0xfd, 0x8d, 0xaa, 0x02, 0x9d, - 0x0c, 0xa0, 0xc8, 0xda, 0xb6, 0x32, 0x51, 0xa1, 0x00, 0xcd, 0x53, 0x99, 0x14, 0xb9, 0x49, 0xc5, 0x78, 0xd4, 0xea, - 0xba, 0xb2, 0xbf, 0x35, 0xcb, 0xe5, 0xc4, 0xf3, 0xbc, 0x0c, 0xec, 0x37, 0xa3, 0xd7, 0x97, 0x8b, 0xc8, 0x36, 0x16, - 0x91, 0xf9, 0x96, 0x91, 0x85, 0x4a, 0x5a, 0xb6, 0xba, 0x07, 0x7f, 0x45, 0x76, 0x23, 0x50, 0x56, 0x7d, 0xe0, 0xcf, - 0xa8, 0x60, 0xb7, 0x31, 0x11, 0x98, 0x6b, 0x03, 0x47, 0x53, 0x1a, 0x30, 0x8c, 0xb2, 0x4b, 0x82, 0xd4, 0xd1, 0xa8, - 0x18, 0xbb, 0x09, 0xe6, 0x68, 0x4d, 0xb3, 0xcf, 0x73, 0x8d, 0x23, 0x8a, 0xf4, 0xde, 0x54, 0x54, 0x62, 0x0b, 0x2b, - 0x38, 0x21, 0x5a, 0x0d, 0x56, 0x5a, 0xcf, 0x3a, 0x6e, 0x8a, 0x71, 0xe1, 0xa0, 0x96, 0xa8, 0xa9, 0xe8, 0x93, 0x46, - 0xb1, 0x4a, 0x10, 0x9e, 0x18, 0x8d, 0x94, 0x97, 0xeb, 0x26, 0xc4, 0x35, 0xde, 0x08, 0xb7, 0xb7, 0xac, 0x98, 0x84, - 0x81, 0xd5, 0x2c, 0x0f, 0x80, 0xa5, 0xf2, 0x6d, 0xe8, 0xde, 0x46, 0x33, 0x95, 0x71, 0x2c, 0x84, 0x73, 0x1b, 0xe1, - 0x16, 0x66, 0x13, 0xc5, 0xb9, 0x92, 0x01, 0x99, 0x54, 0xfb, 0x7a, 0x14, 0x73, 0xb5, 0x0f, 0x1b, 0x48, 0x5c, 0x57, - 0x3c, 0x25, 0x09, 0x82, 0x01, 0x9b, 0x81, 0x72, 0x67, 0xcb, 0x07, 0x0f, 0x60, 0x67, 0xab, 0xd5, 0x06, 0xd1, 0x6d, - 0xd5, 0x3f, 0x91, 0x5f, 0x1a, 0x85, 0xab, 0xd5, 0x8d, 0x40, 0x9e, 0xd6, 0x7c, 0x31, 0x45, 0x3d, 0xc3, 0x71, 0xcf, - 0x5e, 0x42, 0x2b, 0xa9, 0x88, 0x96, 0x25, 0x85, 0xc9, 0x50, 0xa5, 0xd9, 0xea, 0x3e, 0x09, 0x8b, 0x6d, 0x9f, 0x6f, - 0x70, 0x2f, 0x59, 0xa8, 0xc5, 0x74, 0xb9, 0xe4, 0x73, 0x3d, 0x34, 0x43, 0x08, 0x05, 0x99, 0xb4, 0x62, 0xf6, 0xb6, - 0x19, 0x96, 0x07, 0x07, 0x99, 0x35, 0xd0, 0x65, 0xc1, 0x26, 0x3e, 0x78, 0x20, 0x92, 0xb3, 0xbb, 0x44, 0xea, 0x2e, - 0x1f, 0x8c, 0x10, 0xda, 0x30, 0x4b, 0x1b, 0x6d, 0xb0, 0xc6, 0xc3, 0x9b, 0x90, 0x09, 0xa7, 0x18, 0x45, 0x59, 0xe3, - 0x1e, 0x45, 0x4b, 0xad, 0x6a, 0xf8, 0x29, 0x05, 0xe5, 0x11, 0x78, 0x82, 0x51, 0xa1, 0x15, 0xdd, 0x0f, 0x27, 0x14, - 0x1c, 0xc1, 0x46, 0x8b, 0x28, 0xec, 0xc2, 0x3d, 0x2d, 0x45, 0xf4, 0xc0, 0xdb, 0x61, 0xcf, 0xd7, 0xbb, 0x57, 0xec, - 0x80, 0x19, 0x4d, 0x47, 0x3c, 0x9d, 0x9a, 0xba, 0x7c, 0xed, 0x59, 0x73, 0x46, 0x36, 0xf2, 0xb6, 0x8e, 0xad, 0xd5, - 0xff, 0xf6, 0x9a, 0xd1, 0x5d, 0x9a, 0xeb, 0x15, 0x51, 0x5a, 0x48, 0x5f, 0xe5, 0x0f, 0x34, 0x94, 0x99, 0xd9, 0xe6, - 0xbd, 0x76, 0xa6, 0xb6, 0x95, 0xc3, 0x64, 0xaf, 0xd9, 0x2e, 0x6c, 0x3e, 0x43, 0x0d, 0x6d, 0xe5, 0xd8, 0xd0, 0x22, - 0x95, 0xcf, 0xe3, 0x48, 0x03, 0xcb, 0x10, 0xa6, 0x9a, 0x8e, 0x6e, 0x58, 0x1c, 0x97, 0xa5, 0xbf, 0x86, 0xaf, 0x67, - 0x9a, 0xaf, 0x27, 0x86, 0xaf, 0x03, 0xa7, 0x00, 0xbe, 0xae, 0x86, 0x2b, 0xbb, 0x27, 0x1b, 0xa7, 0x33, 0x51, 0x1c, - 0x3d, 0x93, 0x76, 0x34, 0xcc, 0x37, 0x37, 0x10, 0xa0, 0x42, 0xf3, 0xfa, 0xe8, 0x69, 0x27, 0x0c, 0x18, 0x80, 0xca, - 0x85, 0x49, 0x6d, 0x17, 0xc5, 0x47, 0x0f, 0xe1, 0x2c, 0xa7, 0x05, 0x65, 0x9f, 0x3d, 0x07, 0x27, 0x9d, 0xb5, 0x1c, - 0x10, 0x62, 0xb2, 0xf8, 0x57, 0x29, 0x51, 0x66, 0x75, 0x4c, 0xaf, 0x2e, 0x33, 0xab, 0x03, 0x4e, 0x5f, 0xae, 0x2e, - 0xba, 0x9f, 0xd7, 0xcb, 0xe5, 0xb1, 0x62, 0x79, 0xe5, 0x7e, 0xaf, 0x56, 0xde, 0x5a, 0x09, 0xf8, 0xef, 0xb5, 0x89, - 0x92, 0x16, 0xa3, 0x03, 0x0f, 0xb0, 0x31, 0x03, 0x05, 0xb9, 0x5a, 0x74, 0x21, 0xe2, 0x5e, 0x7e, 0xca, 0xc1, 0x23, - 0xdd, 0xf4, 0xaa, 0xff, 0x39, 0x9f, 0xce, 0x40, 0x1b, 0x5b, 0x23, 0xe9, 0x31, 0xd5, 0x13, 0x96, 0xf5, 0xf9, 0x96, - 0xb2, 0x4a, 0x1f, 0x79, 0x1e, 0x2b, 0xd4, 0x54, 0xd8, 0xcb, 0x7b, 0x8d, 0x7c, 0x5e, 0x14, 0x15, 0x8c, 0x63, 0x9b, - 0x53, 0xe5, 0x7c, 0xdd, 0x25, 0x63, 0x2a, 0xde, 0x78, 0x4c, 0xf1, 0x61, 0x06, 0xbc, 0xce, 0x62, 0x3f, 0x86, 0xdc, - 0xed, 0xfd, 0xcf, 0x4b, 0xe4, 0x2c, 0xf3, 0x35, 0xf4, 0x2d, 0xf3, 0xfc, 0x4c, 0x19, 0xd9, 0xf8, 0x6c, 0xb7, 0x35, - 0x5c, 0xd6, 0x69, 0x63, 0xb1, 0x3f, 0xc0, 0x67, 0x9b, 0xaa, 0x23, 0x59, 0x4e, 0x79, 0x44, 0x03, 0x97, 0xcf, 0x68, - 0xe2, 0xe6, 0xe0, 0x55, 0xd5, 0x7b, 0x3f, 0x14, 0xde, 0xf2, 0x6d, 0xd5, 0xbd, 0x1a, 0x9c, 0xe5, 0xe0, 0xfd, 0xfa, - 0x72, 0xd3, 0xf1, 0xfa, 0x1d, 0x4d, 0x33, 0xa9, 0x88, 0x16, 0x3a, 0xed, 0x97, 0xa5, 0x58, 0xfa, 0x32, 0xd8, 0xd9, - 0xbe, 0x34, 0x41, 0xdc, 0xa6, 0xff, 0xc8, 0x3f, 0x72, 0x91, 0x74, 0x0b, 0xff, 0xa8, 0x0f, 0xfc, 0x07, 0xe3, 0x16, - 0x7e, 0x4e, 0x3e, 0x54, 0xbd, 0xc2, 0x91, 0x20, 0xcf, 0x7b, 0xcf, 0x8d, 0xc5, 0xcc, 0x63, 0x36, 0xbc, 0xf3, 0xdc, - 0x98, 0x89, 0x3a, 0x84, 0xde, 0x5c, 0xbc, 0x54, 0x15, 0xe0, 0x52, 0x94, 0xee, 0xec, 0xdc, 0xd8, 0x7a, 0x58, 0x08, - 0xe2, 0xee, 0xc7, 0x4c, 0xec, 0xbb, 0x78, 0x4a, 0xae, 0xe0, 0xc7, 0xfe, 0xd2, 0x7b, 0x15, 0x8a, 0x89, 0x9f, 0x86, - 0x49, 0xc4, 0xa7, 0x1e, 0xaa, 0xb9, 0x2e, 0xf2, 0x33, 0x69, 0x6f, 0x3c, 0x41, 0xf9, 0xfe, 0x15, 0xbe, 0x15, 0xc4, - 0xed, 0xb9, 0xb5, 0x29, 0x7e, 0x2d, 0xc8, 0x55, 0x67, 0x7f, 0x79, 0x2b, 0xf2, 0xee, 0x15, 0xbe, 0x2d, 0x3c, 0xf6, - 0xf8, 0x1b, 0xe2, 0x21, 0xd2, 0xbd, 0xd5, 0xd0, 0x9c, 0xf3, 0xa9, 0xf2, 0xdc, 0xbb, 0x08, 0xbf, 0x87, 0xb8, 0x4a, - 0x5a, 0x72, 0x1b, 0x1d, 0x5a, 0xd9, 0x23, 0x2e, 0x97, 0x2e, 0x02, 0xf7, 0xe0, 0xc0, 0x2a, 0x2b, 0x54, 0x05, 0x7c, - 0x26, 0x48, 0xc5, 0x20, 0xc7, 0x6f, 0x65, 0x84, 0xe6, 0x4c, 0x78, 0x29, 0x32, 0xc3, 0x78, 0xc6, 0x0f, 0xad, 0x8f, - 0x66, 0xda, 0x57, 0x1e, 0x06, 0x9f, 0x09, 0x9a, 0x86, 0x82, 0xa7, 0x03, 0x64, 0xab, 0x1f, 0xf8, 0x6f, 0xe4, 0xaa, - 0xef, 0xfc, 0xa7, 0xcf, 0x7e, 0x1a, 0xfd, 0x94, 0x0e, 0xae, 0xf0, 0x1b, 0x72, 0xd8, 0xf1, 0x7a, 0x81, 0xb7, 0x57, - 0xaf, 0xaf, 0x7e, 0x3a, 0xec, 0xff, 0x23, 0xac, 0xff, 0x72, 0x56, 0xff, 0x71, 0x80, 0x56, 0xde, 0x4f, 0x87, 0xbd, - 0xbe, 0x7e, 0xea, 0xff, 0xa3, 0xfb, 0x53, 0x36, 0xf8, 0xb3, 0x2a, 0xdc, 0x47, 0xe8, 0x70, 0x8c, 0xe7, 0x82, 0x1c, - 0xd6, 0xeb, 0xdd, 0xc3, 0x31, 0x9e, 0x09, 0x72, 0x08, 0x7f, 0xaf, 0xc9, 0x5b, 0x3a, 0x7e, 0x7e, 0x3b, 0xf3, 0xae, - 0xba, 0xab, 0xfd, 0xe5, 0xdf, 0x72, 0x18, 0xb5, 0xff, 0x8f, 0x9f, 0x7e, 0xca, 0xdc, 0x2f, 0xba, 0xe4, 0x70, 0x50, - 0x43, 0x1e, 0x94, 0xfe, 0x99, 0xc8, 0x7f, 0xbd, 0x5e, 0xd0, 0xff, 0x87, 0x86, 0xc2, 0xfd, 0xe2, 0xa7, 0xab, 0x4e, - 0x97, 0x0c, 0x56, 0x9e, 0xbb, 0xfa, 0x02, 0xad, 0x10, 0x5a, 0xed, 0xa3, 0x2b, 0xec, 0x8e, 0x5d, 0x84, 0xc7, 0x82, - 0x1c, 0x7e, 0x71, 0x38, 0xc6, 0x0b, 0x41, 0x0e, 0xdd, 0xc3, 0x31, 0x7e, 0x2e, 0xc8, 0xe1, 0x3f, 0xbc, 0x5e, 0xa0, - 0x3c, 0x6c, 0x2b, 0xe9, 0xde, 0x58, 0x41, 0x70, 0x23, 0x4c, 0x69, 0xb8, 0x12, 0x4c, 0xc4, 0x14, 0xed, 0x1f, 0x32, - 0x7c, 0x21, 0xd1, 0xe4, 0x09, 0x70, 0xc2, 0x80, 0x6d, 0xe7, 0x2d, 0x2f, 0x61, 0xb3, 0x81, 0x66, 0xf6, 0x83, 0x14, - 0x2b, 0x3f, 0x40, 0x16, 0x08, 0xbc, 0x08, 0xe3, 0x39, 0xcd, 0x02, 0x9a, 0x23, 0x3c, 0x24, 0x17, 0xc2, 0x6b, 0x22, - 0xfc, 0x42, 0xc0, 0x8f, 0x16, 0xc2, 0x17, 0x3a, 0x80, 0x09, 0x07, 0x59, 0x11, 0x55, 0xc2, 0x95, 0xc6, 0xe2, 0x22, - 0x3c, 0xdb, 0x52, 0x29, 0x26, 0xe0, 0x5d, 0x40, 0x78, 0xbf, 0x12, 0xee, 0xc4, 0x37, 0xc4, 0x90, 0xc4, 0xbb, 0x94, - 0xd2, 0xef, 0xc3, 0xf8, 0x23, 0x4d, 0xbd, 0x5b, 0xdc, 0x6c, 0x3d, 0xc1, 0xd2, 0x05, 0xbd, 0xd7, 0x44, 0xed, 0x22, - 0x56, 0x75, 0x2e, 0x54, 0x8c, 0x00, 0x84, 0x6c, 0xd5, 0x17, 0x03, 0x3b, 0xbe, 0x97, 0x6e, 0x38, 0xac, 0xd2, 0xf0, - 0xc6, 0x45, 0xd5, 0xb8, 0x28, 0x4b, 0x16, 0x61, 0xcc, 0x22, 0x47, 0xd0, 0xe9, 0x2c, 0x0e, 0x05, 0x75, 0xf4, 0x7a, - 0x9d, 0x10, 0x06, 0x72, 0x0b, 0x95, 0x21, 0xb2, 0x0c, 0xce, 0xc8, 0x04, 0x9c, 0xe0, 0xac, 0x78, 0x10, 0x9d, 0xd2, - 0x6a, 0xc7, 0xd3, 0x32, 0xf8, 0xb5, 0x1e, 0xdf, 0xab, 0x37, 0xc1, 0x11, 0x36, 0x90, 0xe2, 0x39, 0xc3, 0x09, 0x01, - 0x21, 0xda, 0xea, 0xb9, 0x9d, 0x6c, 0x31, 0xee, 0xba, 0x10, 0x9b, 0xe1, 0xe4, 0x8d, 0xf4, 0x0b, 0x41, 0x83, 0x09, - 0x69, 0xb4, 0x27, 0x1d, 0xda, 0x9e, 0xd4, 0x6a, 0x46, 0x87, 0x8e, 0x49, 0xda, 0x9f, 0xa8, 0xee, 0x21, 0x8e, 0xf0, - 0x9c, 0xd4, 0x9b, 0x78, 0x4c, 0x1a, 0xb2, 0x4b, 0x7b, 0xdc, 0x89, 0xf5, 0x34, 0x07, 0x07, 0x1e, 0xf7, 0xe3, 0x30, - 0x13, 0x5f, 0x81, 0xb1, 0x4f, 0xc6, 0x38, 0x22, 0xdc, 0xa7, 0xb7, 0x74, 0xe8, 0xc5, 0x08, 0x47, 0x9a, 0xd3, 0xa0, - 0x36, 0x1a, 0x13, 0xab, 0x19, 0x18, 0x11, 0xe4, 0x4d, 0x2f, 0xea, 0x37, 0x07, 0x84, 0x10, 0x77, 0xaf, 0x5e, 0x77, - 0x7b, 0x9c, 0xcc, 0x45, 0x00, 0x25, 0x96, 0xaa, 0x4c, 0x66, 0x50, 0xd4, 0xb2, 0x8a, 0xbc, 0xe7, 0xc2, 0x17, 0x34, - 0x13, 0x1e, 0x14, 0x83, 0xf9, 0x9f, 0x19, 0xc2, 0x76, 0x3b, 0x87, 0x6e, 0x0d, 0x4a, 0x25, 0x71, 0x22, 0xcc, 0xc9, - 0x35, 0x0a, 0xa2, 0xfe, 0xd1, 0xc0, 0xe6, 0xff, 0xb2, 0x10, 0x26, 0xbf, 0xee, 0x45, 0xfd, 0x86, 0x9c, 0xbc, 0xeb, - 0xf6, 0x3c, 0x4e, 0x32, 0xa5, 0xa0, 0xf5, 0xb2, 0xe0, 0x8d, 0x5c, 0x2a, 0x0a, 0x34, 0x70, 0x7a, 0xde, 0x39, 0xa9, - 0xb7, 0x02, 0x6f, 0x6e, 0x2f, 0xa2, 0x0e, 0x93, 0x69, 0x2c, 0xe0, 0x90, 0x40, 0x7b, 0xcc, 0x09, 0xcc, 0x58, 0x76, - 0xbb, 0x0e, 0xf4, 0xf3, 0x17, 0xee, 0x17, 0xbd, 0x85, 0x08, 0xc6, 0x42, 0x4d, 0xbf, 0x10, 0xab, 0x15, 0xfc, 0x1d, - 0x8b, 0x1e, 0x27, 0xd7, 0xb2, 0x68, 0xae, 0x8b, 0x66, 0x50, 0xf4, 0x26, 0x00, 0x50, 0x71, 0x56, 0x28, 0x59, 0x6a, - 0x4f, 0x16, 0x44, 0xc2, 0x7e, 0x70, 0x90, 0xf6, 0x27, 0xb5, 0xe6, 0x00, 0xfc, 0xfb, 0xa9, 0xc8, 0xbe, 0x67, 0x62, - 0xe2, 0xb9, 0x87, 0x5d, 0x17, 0xf5, 0x5c, 0x07, 0xb6, 0xb6, 0x9d, 0xd4, 0x88, 0xc2, 0x70, 0x5c, 0x7b, 0x2d, 0x82, - 0x79, 0x97, 0x34, 0x7a, 0x1e, 0xd3, 0xfe, 0x3c, 0x84, 0x63, 0xcd, 0x38, 0x1b, 0x78, 0x8e, 0x6a, 0x42, 0xd4, 0xcc, - 0xf3, 0x1c, 0xd5, 0xa6, 0xb5, 0x05, 0x0a, 0xe2, 0xda, 0xb4, 0xe6, 0xcd, 0x09, 0x21, 0xf5, 0x56, 0xd1, 0xcd, 0x48, - 0xbf, 0x09, 0x0a, 0x16, 0xc6, 0xd9, 0xd9, 0x97, 0xc7, 0x21, 0xa9, 0x79, 0x69, 0x9f, 0x0e, 0x56, 0x2b, 0xb7, 0xd3, - 0xeb, 0xba, 0xa8, 0xe6, 0x19, 0x42, 0x3b, 0x34, 0x94, 0x86, 0x10, 0x66, 0x83, 0x5c, 0x87, 0x92, 0xde, 0x55, 0xc2, - 0x46, 0xcb, 0xf2, 0xb0, 0x5b, 0x3c, 0x80, 0xe6, 0x85, 0x1d, 0xa3, 0xf4, 0xd5, 0x19, 0x2c, 0xd3, 0x10, 0x73, 0x42, - 0x1a, 0x98, 0x13, 0xe3, 0xbb, 0x9e, 0x10, 0x51, 0x12, 0x7c, 0x4c, 0xca, 0xe6, 0xb8, 0x1f, 0xe2, 0x68, 0x40, 0x9e, - 0x2a, 0x7b, 0xa4, 0x6d, 0xfc, 0xe2, 0x34, 0x26, 0xef, 0xd6, 0xa2, 0xb7, 0x21, 0xc4, 0x56, 0x6e, 0xfc, 0xe1, 0x3c, - 0x4d, 0x69, 0x22, 0x5e, 0xf3, 0x48, 0xab, 0x69, 0x34, 0x06, 0x4b, 0x09, 0xc2, 0xb2, 0x18, 0x74, 0xb4, 0x96, 0x39, - 0x19, 0xf3, 0x8d, 0xea, 0x31, 0x99, 0x2b, 0xf5, 0x49, 0x06, 0x6b, 0xdb, 0x63, 0x6d, 0x17, 0x7b, 0x08, 0xcf, 0x75, - 0x14, 0xd7, 0xf3, 0x7d, 0x7f, 0xec, 0x0f, 0xa1, 0x1a, 0x26, 0xc8, 0x50, 0x2e, 0xcf, 0x91, 0x97, 0x91, 0x1b, 0x3f, - 0xa1, 0xb7, 0x72, 0x56, 0x0f, 0x95, 0x92, 0xd9, 0x1c, 0xaf, 0xce, 0xa4, 0x2d, 0xd9, 0x4d, 0xe6, 0x27, 0x3c, 0xa2, - 0x80, 0x1e, 0x88, 0xdb, 0xeb, 0xa2, 0x49, 0x98, 0xd9, 0xf1, 0xa9, 0x12, 0xbe, 0xbe, 0xed, 0xbc, 0x1e, 0x83, 0xc7, - 0x57, 0xea, 0x5a, 0x45, 0x63, 0xe5, 0x06, 0x47, 0x88, 0x8d, 0xbc, 0xb1, 0x0f, 0x71, 0x3d, 0x49, 0x42, 0x02, 0x4c, - 0xb9, 0xb1, 0x4d, 0x54, 0xd3, 0x62, 0xcc, 0x05, 0x89, 0xfa, 0xbc, 0x56, 0x93, 0x5e, 0xe8, 0xb9, 0x22, 0x89, 0x31, - 0xc2, 0x8b, 0xe2, 0x6c, 0x99, 0x76, 0x6f, 0x04, 0xa9, 0x4e, 0xe5, 0x2d, 0xaa, 0xee, 0xdc, 0x9a, 0x10, 0x48, 0x7a, - 0x0a, 0x85, 0x37, 0x45, 0xf8, 0x15, 0x39, 0xf4, 0xfa, 0x7e, 0xef, 0x2f, 0x03, 0xd4, 0xf3, 0xfc, 0x3f, 0xa3, 0x43, - 0xc5, 0x39, 0x16, 0xa8, 0x1d, 0xab, 0x39, 0x96, 0x32, 0x7e, 0xd9, 0xc4, 0xd2, 0x93, 0x18, 0x24, 0x38, 0x09, 0xa7, - 0x34, 0x78, 0x05, 0x87, 0xdc, 0x10, 0xce, 0x1b, 0x81, 0x81, 0x92, 0x82, 0x57, 0x9a, 0x97, 0xf8, 0x6e, 0xef, 0xa5, - 0x28, 0x9e, 0x7a, 0x6e, 0xef, 0x43, 0xf9, 0xf4, 0x17, 0xb7, 0xf7, 0x95, 0x08, 0x7e, 0xc9, 0xb5, 0xb7, 0xbb, 0x32, - 0xc7, 0x23, 0x33, 0x47, 0xae, 0xb6, 0xc6, 0xc2, 0xdd, 0x1c, 0x6d, 0x3a, 0x3a, 0xc6, 0x28, 0x67, 0xa3, 0x82, 0x19, - 0x65, 0xbe, 0x08, 0xc7, 0x80, 0x54, 0x6b, 0x0f, 0x32, 0x3b, 0xae, 0x5f, 0xae, 0x18, 0x48, 0xc5, 0xd0, 0x2b, 0x20, - 0x73, 0xdc, 0x6d, 0xa0, 0x65, 0xa5, 0xad, 0xd4, 0x99, 0xaa, 0x71, 0xf4, 0x82, 0x4f, 0x2f, 0x48, 0xa3, 0xbd, 0xe8, - 0x8c, 0xdb, 0x8b, 0x5a, 0x0d, 0x65, 0x86, 0xb4, 0xe6, 0xfd, 0xc5, 0x00, 0x7f, 0x03, 0x4e, 0x3d, 0x9b, 0x96, 0x70, - 0x65, 0x79, 0x2d, 0xbd, 0xbc, 0x5a, 0x2d, 0xc9, 0x51, 0xdb, 0xea, 0x3a, 0x56, 0x5d, 0xf3, 0x5c, 0xe1, 0x64, 0x9d, - 0xd4, 0x4e, 0x91, 0x2c, 0x81, 0x64, 0x28, 0x42, 0xc8, 0xad, 0x40, 0x5b, 0x47, 0x85, 0x31, 0xa1, 0xbb, 0x3c, 0xb3, - 0xc0, 0x3e, 0x95, 0x94, 0xf0, 0x00, 0x0b, 0xd0, 0xb5, 0xf0, 0x04, 0x4f, 0xf1, 0xbc, 0xd6, 0x94, 0x64, 0x5e, 0x6f, - 0xb6, 0xab, 0x63, 0x3d, 0x2e, 0xc7, 0xc2, 0xf3, 0x1a, 0x99, 0x16, 0x58, 0xca, 0x93, 0x5a, 0x2d, 0xaf, 0x06, 0x3b, - 0xcd, 0xc9, 0xad, 0x04, 0x20, 0x6e, 0xd7, 0x93, 0x32, 0x8c, 0x84, 0x2d, 0x65, 0x2a, 0xf3, 0x59, 0x92, 0xd0, 0x14, - 0xa4, 0x28, 0x11, 0x98, 0xe5, 0x79, 0x29, 0xd9, 0x41, 0x8c, 0x62, 0x4a, 0x52, 0xe0, 0x3c, 0xd2, 0xee, 0xc2, 0x09, - 0xe6, 0x78, 0x22, 0xf9, 0x06, 0x21, 0xe4, 0xc2, 0xa4, 0xb3, 0x08, 0xc9, 0x83, 0x62, 0xc2, 0x2c, 0x99, 0x94, 0x11, - 0xea, 0x5f, 0xee, 0x9f, 0xf3, 0x7b, 0x6d, 0xb2, 0x3e, 0x1b, 0x04, 0xb2, 0x59, 0xac, 0x39, 0x57, 0x48, 0xde, 0x7b, - 0x02, 0x15, 0xd1, 0x11, 0x5f, 0x32, 0xc0, 0x67, 0x2c, 0xa5, 0x52, 0x07, 0xdf, 0x37, 0x76, 0x5f, 0x5c, 0x55, 0x20, - 0x63, 0xdb, 0x7b, 0x03, 0x88, 0x0c, 0xc1, 0xb9, 0x93, 0x90, 0x8d, 0x66, 0x97, 0xfb, 0x67, 0x6f, 0xb6, 0xd9, 0xc0, - 0xab, 0x95, 0xb6, 0x7e, 0xa5, 0x6e, 0x83, 0xc3, 0x12, 0xd2, 0x58, 0xff, 0x08, 0xbc, 0x58, 0xaa, 0x48, 0xa1, 0x97, - 0x02, 0x15, 0x5d, 0xee, 0x9f, 0xbd, 0xf3, 0x52, 0xe9, 0x5b, 0x42, 0xd8, 0x5e, 0xb6, 0xc7, 0x89, 0x37, 0x21, 0x14, - 0xa9, 0xb5, 0x17, 0xac, 0x8b, 0x5b, 0x02, 0x3c, 0x98, 0xc8, 0x4a, 0xb0, 0x20, 0xfa, 0x6c, 0x40, 0x62, 0x8d, 0x01, - 0x12, 0x23, 0x1c, 0x57, 0xec, 0x32, 0x02, 0x1b, 0x20, 0xe7, 0xba, 0x80, 0x9d, 0xf0, 0x95, 0xea, 0x87, 0x70, 0x2c, - 0x67, 0x15, 0xb9, 0x12, 0x1e, 0xaf, 0x36, 0xb2, 0xd2, 0x4a, 0x73, 0xf4, 0x3b, 0xb0, 0x9d, 0xcc, 0xc3, 0x6b, 0x62, - 0x2c, 0x09, 0x5d, 0xf0, 0xcc, 0xa4, 0x8f, 0x5d, 0xee, 0x9f, 0xbd, 0xd2, 0x19, 0x64, 0xb3, 0xd0, 0xf0, 0xfb, 0x0d, - 0x13, 0xf3, 0xec, 0x95, 0x5f, 0xd6, 0xca, 0xc6, 0x97, 0xfb, 0x67, 0xef, 0xb7, 0x35, 0x83, 0xf2, 0x7c, 0x5e, 0xda, - 0xf8, 0x12, 0xbe, 0x25, 0x8d, 0x83, 0xa5, 0x16, 0x0e, 0x01, 0xcb, 0xb1, 0x14, 0x48, 0x41, 0x96, 0x17, 0xae, 0x91, - 0x67, 0x38, 0x21, 0x32, 0x0c, 0x54, 0xdd, 0x35, 0xad, 0xe6, 0x31, 0x9e, 0x5c, 0x0c, 0xf9, 0x8c, 0xee, 0x88, 0x0d, - 0xdd, 0x22, 0x9f, 0x4d, 0x21, 0x75, 0x46, 0x82, 0xce, 0xf0, 0x5e, 0x03, 0xb5, 0xab, 0xe2, 0x2b, 0x91, 0x44, 0xca, - 0x2b, 0xb2, 0x05, 0x4f, 0x48, 0x03, 0xc7, 0xa4, 0x81, 0x43, 0x92, 0xf5, 0x1b, 0x4a, 0x40, 0xb4, 0xc3, 0x62, 0x5c, - 0x25, 0x66, 0x20, 0x2b, 0x4c, 0x9f, 0x56, 0x25, 0x80, 0xa3, 0x76, 0x28, 0x7d, 0x8f, 0x52, 0xa6, 0x47, 0x92, 0x2c, - 0xde, 0x7a, 0x1c, 0x73, 0x39, 0xf0, 0x05, 0xbb, 0x8e, 0x21, 0xb1, 0x04, 0x56, 0x85, 0x05, 0x0a, 0x8a, 0xa6, 0x4d, - 0xdd, 0x34, 0xf4, 0xe5, 0x3e, 0x71, 0x1c, 0xfa, 0xc0, 0xb9, 0x71, 0xa8, 0xf3, 0x70, 0xb2, 0xcd, 0x2e, 0x8f, 0x0e, - 0x0e, 0x3c, 0xd5, 0xe9, 0x67, 0xe1, 0x71, 0x53, 0x5f, 0x46, 0xee, 0xbe, 0x53, 0xbc, 0x22, 0x42, 0x12, 0xfe, 0x5a, - 0x2d, 0x1e, 0xe4, 0x10, 0x86, 0xf6, 0xc2, 0x2a, 0x06, 0x0d, 0xf0, 0x52, 0xd7, 0xab, 0x2e, 0xbf, 0x56, 0x2b, 0xa2, - 0xb4, 0x55, 0x6c, 0xdd, 0xe2, 0x24, 0x5f, 0x78, 0x45, 0xea, 0x4f, 0x63, 0x23, 0x5f, 0xca, 0x80, 0x80, 0x98, 0x4d, - 0xb3, 0xcc, 0x2c, 0xc6, 0x3a, 0x12, 0x0c, 0xda, 0x7d, 0xa5, 0xb3, 0x16, 0xb0, 0xcc, 0xae, 0xd2, 0x8d, 0x0c, 0x3b, - 0x6b, 0xa1, 0xc0, 0x34, 0x82, 0xa8, 0x14, 0x34, 0xaa, 0xe5, 0x9a, 0xbc, 0xdf, 0x6e, 0xe6, 0x5c, 0xe2, 0x0c, 0x69, - 0x27, 0x97, 0x84, 0x42, 0x22, 0xab, 0x55, 0x20, 0xe5, 0x05, 0x99, 0xed, 0x26, 0xf9, 0x33, 0x8b, 0xe4, 0x9f, 0x12, - 0x6a, 0x91, 0xbf, 0x72, 0x71, 0xf8, 0x5c, 0x3b, 0x17, 0x32, 0x53, 0x75, 0x3e, 0x23, 0xe0, 0x44, 0xab, 0x62, 0xb4, - 0x12, 0x56, 0xdc, 0xc1, 0x50, 0xec, 0x13, 0x22, 0xdd, 0x90, 0xd8, 0xc4, 0x80, 0xbd, 0x32, 0xa8, 0x06, 0x53, 0x6f, - 0xf3, 0xe9, 0xd9, 0x1c, 0xf0, 0xec, 0xfd, 0xfd, 0xf1, 0xd0, 0xf3, 0xd9, 0xe6, 0xc9, 0xb5, 0x72, 0x3f, 0x61, 0xd5, - 0xd6, 0xc1, 0xad, 0x66, 0x82, 0xc2, 0xfc, 0x45, 0x1c, 0xbb, 0xca, 0x7c, 0xd6, 0x0e, 0xa1, 0x91, 0x7f, 0x00, 0x6d, - 0xb3, 0x29, 0x5b, 0x50, 0x6b, 0x58, 0xe0, 0x47, 0x2a, 0x03, 0x35, 0x4c, 0x77, 0xb0, 0x8f, 0x33, 0xd9, 0x80, 0x26, - 0xd1, 0xf6, 0xea, 0xa7, 0xb9, 0x26, 0x13, 0x05, 0x1a, 0x5a, 0x02, 0xff, 0x53, 0x24, 0x0f, 0x74, 0x23, 0xe5, 0x02, - 0x20, 0x68, 0x26, 0xf1, 0x54, 0x22, 0xcc, 0x75, 0x4b, 0xef, 0xfb, 0x8b, 0x3d, 0x42, 0x66, 0xa5, 0xf7, 0xf1, 0x6d, - 0x99, 0x7a, 0x05, 0x64, 0x81, 0x02, 0x30, 0x1f, 0x8b, 0x02, 0x15, 0xbe, 0xbc, 0x30, 0xcd, 0xa5, 0x09, 0xe9, 0x97, - 0x1a, 0xb7, 0x15, 0xda, 0x94, 0x6e, 0x39, 0x55, 0x6f, 0xd0, 0xb0, 0x56, 0xbb, 0x0f, 0xb5, 0x6f, 0x85, 0x84, 0x11, - 0x9e, 0xdf, 0xc9, 0xd6, 0x66, 0xdc, 0xfc, 0xe3, 0x7a, 0xfe, 0xca, 0xda, 0xa6, 0xf8, 0x2c, 0xc9, 0x68, 0x2a, 0x9e, - 0xd2, 0x11, 0x4f, 0x21, 0x66, 0x51, 0xe0, 0x04, 0xe5, 0xfb, 0x96, 0xdf, 0x4e, 0xae, 0xcf, 0x0a, 0x14, 0xac, 0x2d, - 0x50, 0xfe, 0xfa, 0x28, 0x83, 0xd6, 0x97, 0xeb, 0xbd, 0x66, 0x07, 0x07, 0xef, 0x4b, 0x34, 0x69, 0x28, 0x25, 0x14, - 0x16, 0xd3, 0x52, 0x2a, 0x8d, 0x8e, 0xe4, 0xee, 0x7b, 0x85, 0x13, 0xc0, 0x30, 0x0c, 0x9b, 0xf7, 0xbc, 0x20, 0x22, - 0x1f, 0xaf, 0xb3, 0x78, 0xed, 0x9c, 0x60, 0xb6, 0xe1, 0x02, 0x1c, 0x1e, 0x4c, 0x6d, 0xe5, 0x2d, 0xca, 0xca, 0x64, - 0xd8, 0x02, 0x86, 0x73, 0x40, 0x96, 0x27, 0xcd, 0x10, 0x8b, 0x02, 0xb7, 0x9a, 0x25, 0xe7, 0xa0, 0x57, 0x4e, 0x70, - 0xe6, 0x4f, 0x20, 0xfd, 0xb5, 0x72, 0x64, 0x11, 0xc2, 0x2a, 0x31, 0xc7, 0x4a, 0x25, 0x38, 0x7b, 0xb1, 0xcd, 0xa5, - 0x6c, 0x88, 0x9a, 0x4a, 0xa9, 0x23, 0x5b, 0xa0, 0xa2, 0x83, 0xbf, 0xf0, 0x98, 0x56, 0xdc, 0x4c, 0xdc, 0x4c, 0xfa, - 0x25, 0x85, 0xa7, 0x82, 0x51, 0x20, 0x33, 0xb8, 0x3f, 0xf7, 0x2a, 0x53, 0xb7, 0xb9, 0xec, 0x86, 0x35, 0xe2, 0x26, - 0x36, 0x9a, 0xb8, 0x8c, 0xeb, 0x9d, 0x97, 0xbc, 0x74, 0x5f, 0x65, 0x50, 0x0b, 0xc3, 0x05, 0xcb, 0x44, 0x12, 0x6b, - 0xf9, 0xfb, 0x2a, 0x29, 0xba, 0x68, 0x84, 0xa9, 0x04, 0xe3, 0x9d, 0xdc, 0x03, 0x9a, 0xc3, 0xdf, 0xe5, 0x99, 0xb0, - 0x76, 0xd4, 0x38, 0xb1, 0xe5, 0x9c, 0x96, 0xd4, 0x7f, 0x0b, 0xa9, 0x2e, 0xeb, 0x67, 0xfe, 0x85, 0x94, 0x85, 0x0c, - 0x67, 0x15, 0xc6, 0x9e, 0x48, 0xc6, 0x8e, 0x40, 0x4f, 0x33, 0x89, 0xdf, 0x3d, 0x9d, 0xf1, 0xc2, 0xb4, 0x94, 0xd3, - 0x24, 0xf6, 0x4d, 0x11, 0x2d, 0xb7, 0x7e, 0xaf, 0xed, 0x46, 0xc0, 0x08, 0x64, 0x01, 0x61, 0xcd, 0xd9, 0x13, 0x84, - 0xb3, 0x5a, 0xad, 0x9d, 0x75, 0x68, 0xe9, 0x24, 0x29, 0x61, 0x64, 0x10, 0xd0, 0x05, 0x82, 0xaf, 0xc8, 0x50, 0x08, - 0xf9, 0x9b, 0xcc, 0xec, 0x0c, 0x7c, 0xed, 0x67, 0x6f, 0x3d, 0x9b, 0xab, 0xd9, 0x6d, 0x8b, 0xa0, 0x29, 0xac, 0xc7, - 0x2b, 0x03, 0x2e, 0xdf, 0xdc, 0x9f, 0xe0, 0x01, 0x70, 0xef, 0x35, 0x31, 0xa4, 0xa2, 0xa1, 0xb6, 0x50, 0x2c, 0xa1, - 0x38, 0x7d, 0x6d, 0x54, 0x66, 0x25, 0xda, 0x93, 0xb5, 0x45, 0x69, 0xcc, 0x0a, 0x92, 0xe5, 0x79, 0x46, 0xcb, 0xf0, - 0xfe, 0x5a, 0xfa, 0xa5, 0x14, 0x2e, 0x9b, 0xde, 0xf6, 0xf3, 0x19, 0x11, 0xd8, 0x22, 0xd4, 0x6f, 0x76, 0xc5, 0x3e, - 0x4a, 0x30, 0xe1, 0x5c, 0x6b, 0xa1, 0xf8, 0xcb, 0x36, 0xa1, 0x88, 0x13, 0x7d, 0xe4, 0xa5, 0x40, 0x6c, 0x3e, 0x40, - 0x20, 0x6a, 0x37, 0xbb, 0x91, 0x89, 0xa0, 0x8e, 0x54, 0x64, 0x62, 0x75, 0x4b, 0x49, 0x82, 0x99, 0xde, 0x8d, 0x6e, - 0x6b, 0xb5, 0x62, 0xfd, 0x06, 0xb8, 0x91, 0x5c, 0x17, 0x7e, 0x36, 0xd5, 0x4f, 0x8b, 0x13, 0x2b, 0x37, 0xb0, 0xc7, - 0x0a, 0x93, 0x05, 0xf9, 0x90, 0xe0, 0xec, 0xc9, 0xa4, 0x2c, 0x49, 0xd3, 0x9a, 0x82, 0x34, 0x81, 0x13, 0x56, 0x84, - 0x99, 0x00, 0x62, 0x29, 0x2b, 0xb4, 0x01, 0xe9, 0x6d, 0xcd, 0xfd, 0x33, 0xe6, 0xe5, 0xa7, 0x35, 0xd1, 0x8a, 0x5c, - 0x51, 0xea, 0x43, 0x25, 0xdf, 0x40, 0x43, 0xa0, 0xf5, 0xc3, 0x3d, 0x69, 0x82, 0x96, 0xa2, 0x1c, 0xd9, 0x72, 0x08, - 0x37, 0xc0, 0x89, 0xb6, 0xf7, 0x5e, 0x45, 0x78, 0xb7, 0x48, 0x13, 0xcc, 0x2d, 0xba, 0x7e, 0x41, 0x44, 0x85, 0x95, - 0x4c, 0x88, 0xb6, 0x94, 0x70, 0x28, 0xc9, 0x54, 0x90, 0xa4, 0xdf, 0x18, 0x80, 0x02, 0xda, 0x8e, 0x3b, 0x49, 0x69, - 0x02, 0xc7, 0xb5, 0x1a, 0x0a, 0xcd, 0xac, 0x93, 0x3e, 0xab, 0xc5, 0x03, 0x4c, 0x71, 0xac, 0x0c, 0x93, 0x8b, 0x83, - 0x03, 0x2f, 0x2c, 0xe7, 0xed, 0xc7, 0x03, 0x84, 0xf9, 0x6a, 0xe5, 0x49, 0xb0, 0x42, 0xb4, 0x5a, 0x85, 0x36, 0x58, - 0xb2, 0x1a, 0xba, 0xcd, 0x7a, 0x82, 0xcc, 0xa4, 0x00, 0x9c, 0x01, 0x84, 0x35, 0xe2, 0x85, 0xda, 0xbd, 0x17, 0x82, - 0x3b, 0xaa, 0x96, 0xf4, 0xe3, 0x5a, 0x73, 0x60, 0x31, 0xae, 0x7e, 0x3c, 0x20, 0x61, 0xce, 0x0f, 0x0e, 0xf6, 0x32, - 0x2d, 0x22, 0x3f, 0x80, 0x28, 0xfb, 0x20, 0x25, 0x8b, 0x1a, 0xd0, 0xde, 0x8d, 0x75, 0x67, 0x40, 0x41, 0x51, 0x7a, - 0x5b, 0x4d, 0xbb, 0x4a, 0x16, 0x44, 0xd1, 0x08, 0xeb, 0x60, 0x70, 0x0f, 0x2c, 0xfb, 0x82, 0xcc, 0x5f, 0x8a, 0x22, - 0xc7, 0xfa, 0x97, 0xad, 0x99, 0xd5, 0xbe, 0xef, 0x87, 0xe9, 0x58, 0xc6, 0x32, 0x4c, 0x18, 0x56, 0x12, 0xff, 0x91, - 0x06, 0xd3, 0x9a, 0xb8, 0x5f, 0xcc, 0x35, 0x20, 0x0a, 0x7c, 0xa3, 0xda, 0x98, 0xbb, 0x24, 0xcf, 0xb6, 0x7a, 0x19, - 0x14, 0x24, 0x1f, 0x7e, 0x2b, 0x24, 0xc7, 0x1a, 0x12, 0x45, 0x1e, 0x6b, 0x38, 0xdb, 0x81, 0x8b, 0x67, 0x62, 0x0d, - 0x67, 0xbb, 0x71, 0x6b, 0x30, 0xf5, 0xd5, 0x2e, 0xf8, 0x2c, 0xde, 0xa0, 0x00, 0x2d, 0x0b, 0x2c, 0x28, 0x4f, 0xd6, - 0x75, 0x2f, 0xc5, 0x4a, 0x41, 0x98, 0x0a, 0xe2, 0xb1, 0xea, 0x01, 0x28, 0xb5, 0x51, 0xcb, 0xf0, 0x65, 0xc1, 0x0c, - 0x59, 0x2e, 0x81, 0x6a, 0xea, 0x0a, 0x90, 0x93, 0xf6, 0xb6, 0xcf, 0x0e, 0x0e, 0xc0, 0x36, 0x00, 0x25, 0xce, 0x1f, - 0x86, 0x33, 0x31, 0x4f, 0x41, 0x95, 0xca, 0xcc, 0x6f, 0x28, 0x86, 0x5b, 0x20, 0xb2, 0x0c, 0x7e, 0x40, 0xc1, 0x2c, - 0xcc, 0x32, 0xb6, 0x50, 0x65, 0xfa, 0x37, 0xe6, 0xc4, 0x90, 0x72, 0xa6, 0x74, 0xc2, 0x04, 0xb5, 0x13, 0x4d, 0xa7, - 0x55, 0xb4, 0x3d, 0x5f, 0xd0, 0x44, 0xbc, 0x64, 0x99, 0xa0, 0x09, 0x2c, 0xbf, 0xa4, 0x38, 0x58, 0x51, 0x86, 0xe0, - 0xc0, 0x56, 0x7a, 0x85, 0x51, 0x74, 0x6f, 0x17, 0x51, 0xd5, 0x81, 0x26, 0x61, 0x12, 0xc5, 0x6a, 0x12, 0x3b, 0x9f, - 0xd1, 0xe4, 0x70, 0x16, 0x2d, 0xed, 0x7c, 0x9a, 0x52, 0xd9, 0x90, 0xdc, 0xdd, 0x63, 0xc4, 0x48, 0x02, 0x23, 0x3d, - 0xef, 0xd5, 0x5a, 0x20, 0xe2, 0xbd, 0x63, 0x13, 0xec, 0x95, 0x60, 0x61, 0x71, 0x54, 0xbf, 0x0a, 0xa7, 0xa1, 0x9b, - 0x9f, 0xb7, 0x5e, 0x69, 0xdb, 0x26, 0x1c, 0x24, 0x9d, 0x3c, 0xda, 0x6d, 0x59, 0xbd, 0x32, 0x92, 0xc3, 0x48, 0x0b, - 0xf6, 0x50, 0xc6, 0x8c, 0x96, 0x86, 0xbc, 0x90, 0x39, 0x8a, 0x23, 0x41, 0x3e, 0xc0, 0x9d, 0xa1, 0x17, 0x62, 0x1a, - 0xaf, 0x5d, 0x8d, 0x69, 0x8f, 0x0a, 0xed, 0x7f, 0x24, 0xbc, 0x77, 0xf8, 0x2d, 0x04, 0x76, 0x7f, 0x2c, 0x9b, 0x6f, - 0x06, 0x74, 0x7f, 0x2c, 0x11, 0xf4, 0x63, 0xb0, 0xd1, 0xce, 0x0a, 0xe4, 0xb6, 0xfc, 0x53, 0xbf, 0xe1, 0x1a, 0x6d, - 0xe9, 0x17, 0x15, 0x46, 0x52, 0x99, 0x96, 0xf2, 0x3c, 0xe0, 0x32, 0x4f, 0x0d, 0xf2, 0xe5, 0xaa, 0x16, 0x12, 0xd5, - 0x19, 0x86, 0x4a, 0x87, 0xdf, 0xb5, 0x3d, 0x5a, 0xc6, 0x24, 0xca, 0xce, 0xf8, 0x26, 0x4c, 0xc5, 0x3e, 0x9c, 0x32, - 0xbe, 0x71, 0x0f, 0x6f, 0x42, 0xc0, 0x83, 0xf6, 0xb0, 0x29, 0x2c, 0x63, 0x3b, 0x53, 0xf7, 0x80, 0xec, 0xf1, 0x09, - 0x37, 0xba, 0x5b, 0xd5, 0xca, 0xf8, 0x06, 0xec, 0x7f, 0x84, 0x27, 0xe6, 0x72, 0x1c, 0xd5, 0x1c, 0x98, 0x06, 0xcb, - 0xbc, 0x70, 0x0a, 0x70, 0xa5, 0xbc, 0xa5, 0x08, 0xf3, 0x5c, 0x06, 0xb8, 0xbf, 0xc6, 0xdf, 0x6a, 0x96, 0xb8, 0x5f, - 0x70, 0x9c, 0xb3, 0x87, 0x72, 0x44, 0x05, 0x7e, 0x11, 0xbf, 0x07, 0x3a, 0x96, 0x14, 0x9a, 0x1b, 0x2a, 0x7a, 0xc6, - 0xf5, 0x42, 0x76, 0xa6, 0xa5, 0x62, 0x5a, 0xa4, 0xd4, 0xc8, 0x69, 0xb6, 0xe4, 0x71, 0x1a, 0x2b, 0x5b, 0x14, 0xa7, - 0xaa, 0x32, 0x2f, 0xda, 0x81, 0xc5, 0x32, 0xb4, 0xb8, 0x5a, 0x79, 0x55, 0x54, 0x13, 0x66, 0x45, 0x32, 0x10, 0x66, - 0x56, 0x46, 0x45, 0x45, 0xb3, 0x56, 0x7d, 0x3c, 0xb4, 0x9e, 0x50, 0x64, 0x74, 0xf3, 0x0a, 0x1c, 0xb6, 0x0b, 0x41, - 0x75, 0xb7, 0x7d, 0x0a, 0x58, 0xad, 0xae, 0x98, 0xc8, 0xc2, 0xd0, 0x2f, 0x45, 0xaa, 0x6c, 0x99, 0xd3, 0xba, 0x05, - 0xbf, 0xe8, 0x9e, 0x64, 0x59, 0x8d, 0xba, 0xcd, 0x7a, 0x2b, 0xd9, 0xe8, 0x19, 0xdf, 0x95, 0x6c, 0x54, 0xd1, 0x76, - 0xf7, 0x1a, 0xe8, 0xfe, 0xb4, 0x54, 0x35, 0xd7, 0xf6, 0x26, 0xbf, 0x61, 0xba, 0x26, 0xd0, 0xa6, 0x42, 0xb3, 0xe1, - 0x2a, 0x17, 0x79, 0xbe, 0x5f, 0x5c, 0x26, 0x90, 0xb9, 0x3b, 0xfb, 0x8a, 0xfe, 0xb5, 0xd5, 0x28, 0xaf, 0xe3, 0x7a, - 0x5f, 0x93, 0x71, 0xcc, 0xaf, 0xc3, 0xf8, 0x1d, 0xcc, 0x57, 0x56, 0xbe, 0xb8, 0x8b, 0xd2, 0x50, 0x50, 0xcd, 0x5d, - 0x4a, 0x18, 0xbe, 0xb6, 0x60, 0xf8, 0x5a, 0xf1, 0xe9, 0xb2, 0x3f, 0x5e, 0xbe, 0x2c, 0x06, 0x08, 0xf6, 0x73, 0xc3, - 0x32, 0x2e, 0xc5, 0xf6, 0x39, 0xd6, 0x59, 0xd8, 0x65, 0xc1, 0xc2, 0x2e, 0x85, 0xb7, 0x3e, 0x94, 0xe7, 0x7d, 0xbb, - 0x7d, 0x94, 0x4d, 0xce, 0xf6, 0x6d, 0x79, 0xf0, 0xbf, 0x0d, 0xee, 0xed, 0x63, 0x71, 0xb9, 0x23, 0xff, 0x48, 0xa6, - 0xab, 0x28, 0x90, 0x5f, 0x40, 0xda, 0x81, 0x20, 0x5d, 0xeb, 0xce, 0x41, 0x29, 0xa7, 0x4c, 0x22, 0x90, 0x37, 0x9c, - 0x67, 0x82, 0x4f, 0xf5, 0x98, 0x99, 0xbe, 0x66, 0x24, 0x2b, 0xc1, 0x15, 0x2d, 0xa3, 0xed, 0x41, 0xf5, 0x22, 0xd7, - 0xf2, 0x23, 0x4b, 0xa2, 0x20, 0xc3, 0x5a, 0x8a, 0x64, 0x41, 0x92, 0x13, 0x93, 0x6c, 0xbc, 0x59, 0x87, 0x47, 0x2c, - 0x61, 0xd9, 0x84, 0xa6, 0x1e, 0x47, 0xcb, 0x5d, 0x93, 0x71, 0x08, 0xc8, 0xa8, 0xc9, 0xf0, 0x77, 0xe5, 0x85, 0x3f, - 0x1f, 0x46, 0x03, 0x3f, 0xd0, 0x94, 0x8a, 0x09, 0x8f, 0x20, 0x31, 0xc5, 0x8f, 0x8a, 0x1b, 0x4d, 0x07, 0x07, 0x7b, - 0x9e, 0x2b, 0xdd, 0x12, 0x70, 0xf5, 0xdb, 0xae, 0x41, 0xbd, 0x25, 0x5c, 0xcf, 0x29, 0xa7, 0xa6, 0x68, 0x49, 0xd7, - 0x6f, 0xb2, 0x08, 0xff, 0x23, 0xbd, 0xc3, 0x29, 0xca, 0xf3, 0x40, 0x41, 0xed, 0x8e, 0x18, 0x8d, 0x23, 0x17, 0x7f, - 0xa4, 0x77, 0x41, 0x71, 0x5b, 0x5c, 0x5e, 0x6e, 0x96, 0x1b, 0xe8, 0xf2, 0x9b, 0xc4, 0xc5, 0xe5, 0x24, 0xc1, 0x32, - 0xc7, 0x3c, 0x65, 0x63, 0x20, 0xce, 0xaf, 0xe9, 0x5d, 0xa0, 0xc6, 0x63, 0xd6, 0x65, 0x3d, 0xb4, 0x34, 0xa8, 0xf7, - 0xad, 0x62, 0x7b, 0x1b, 0xb4, 0x41, 0xd1, 0x97, 0x7d, 0x07, 0xa4, 0xd2, 0xae, 0x34, 0x0f, 0x11, 0xca, 0x1f, 0xba, - 0x14, 0xfc, 0xa5, 0x2d, 0xda, 0x44, 0x25, 0xf5, 0x75, 0xad, 0x13, 0x85, 0x0e, 0x65, 0xae, 0xc7, 0xa5, 0x97, 0x9a, - 0x53, 0xa7, 0xef, 0x20, 0x58, 0x8e, 0xb0, 0x2f, 0x85, 0x1e, 0x34, 0xf8, 0x4e, 0xa5, 0x84, 0x94, 0x91, 0xa4, 0xa7, - 0x65, 0x3f, 0xe7, 0xd2, 0x03, 0xbc, 0x43, 0x4a, 0x4b, 0x28, 0xaf, 0x63, 0xe6, 0x26, 0x5d, 0xf4, 0x7b, 0x41, 0xbc, - 0xa5, 0x59, 0x42, 0x90, 0xda, 0x58, 0x14, 0x39, 0x50, 0xa1, 0xa6, 0x2f, 0x95, 0x01, 0xc8, 0x46, 0x1e, 0xdb, 0x90, - 0x9a, 0x89, 0x94, 0x9a, 0xbe, 0x85, 0xf1, 0x1d, 0x52, 0x92, 0x4a, 0x64, 0x48, 0x25, 0x52, 0x0a, 0x3d, 0xbd, 0xb9, - 0x9a, 0x84, 0xec, 0x0d, 0x2d, 0xae, 0xcf, 0xa9, 0x3d, 0x4f, 0x2a, 0x60, 0x79, 0x72, 0x1c, 0x94, 0x07, 0xb0, 0x24, - 0xaa, 0x1a, 0xe4, 0xc6, 0x9d, 0x93, 0x9a, 0xfc, 0x56, 0x8f, 0xfb, 0x66, 0x59, 0xc4, 0xa0, 0xc4, 0x9b, 0xa0, 0x65, - 0xea, 0x4d, 0x70, 0x02, 0xf9, 0x88, 0x3c, 0x2f, 0xe0, 0xa7, 0xf6, 0x6e, 0x54, 0xb2, 0x95, 0xb7, 0x5f, 0xf1, 0x03, - 0x65, 0x5e, 0x40, 0x8e, 0x26, 0x4e, 0x0d, 0x4f, 0x49, 0x3d, 0x79, 0xd7, 0xce, 0xda, 0xb6, 0x1f, 0x75, 0x8a, 0x8e, - 0x06, 0xec, 0x7b, 0xe1, 0x2d, 0xad, 0x55, 0xd8, 0x77, 0xb9, 0xf5, 0x95, 0x3f, 0x1d, 0xec, 0x2b, 0x93, 0x48, 0xbd, - 0x8c, 0xac, 0x49, 0x9c, 0xfb, 0x73, 0x2d, 0x7f, 0x9e, 0xd3, 0xf4, 0xee, 0x82, 0x42, 0xae, 0x33, 0x87, 0xbb, 0xbe, - 0xe5, 0x36, 0x94, 0x79, 0xea, 0xbd, 0x44, 0x2a, 0x2b, 0x79, 0xf5, 0x12, 0xe0, 0xfa, 0x15, 0xc1, 0x5c, 0x46, 0x1b, - 0x2d, 0x47, 0x8c, 0x3a, 0x2d, 0x74, 0xe7, 0xe5, 0x49, 0xda, 0x66, 0xe0, 0x5f, 0x2b, 0x31, 0xad, 0x83, 0x05, 0x98, - 0xdb, 0x17, 0x52, 0xfb, 0xd9, 0x60, 0xdd, 0x2b, 0x03, 0x45, 0x10, 0xbe, 0x4b, 0x76, 0x2f, 0x75, 0x5b, 0xd6, 0xec, - 0xee, 0xa5, 0x56, 0x82, 0x7e, 0x32, 0xe5, 0x07, 0xeb, 0x79, 0x8a, 0xcb, 0xcb, 0x2c, 0xcf, 0x51, 0x0e, 0xe0, 0xfd, - 0xd0, 0xf6, 0xbc, 0x1f, 0x74, 0xd2, 0xa0, 0x0f, 0xb1, 0xd8, 0x8b, 0x98, 0x1b, 0x26, 0x5e, 0xce, 0xff, 0xc3, 0xc6, - 0xfc, 0x3f, 0x58, 0x57, 0x4e, 0xc1, 0x34, 0x1a, 0x27, 0x34, 0x32, 0xac, 0x13, 0x29, 0x02, 0x94, 0x7a, 0x5b, 0x26, - 0xc8, 0xc7, 0xab, 0x00, 0x34, 0xae, 0xe5, 0x88, 0x27, 0xa2, 0x3e, 0x0a, 0xa7, 0x2c, 0xbe, 0x0b, 0xe6, 0xac, 0x3e, - 0xe5, 0x09, 0xcf, 0x66, 0xe1, 0x90, 0xe2, 0xec, 0x2e, 0x13, 0x74, 0x5a, 0x9f, 0x33, 0xfc, 0x82, 0xc6, 0x0b, 0x2a, - 0xd8, 0x30, 0xc4, 0xee, 0x59, 0xca, 0xc2, 0xd8, 0x79, 0x1d, 0xa6, 0x29, 0xbf, 0x71, 0xf1, 0x5b, 0x7e, 0xcd, 0x05, - 0xc7, 0x6f, 0x6e, 0xef, 0xc6, 0x34, 0xc1, 0xef, 0xaf, 0xe7, 0x89, 0x98, 0xe3, 0x2c, 0x4c, 0xb2, 0x7a, 0x46, 0x53, - 0x36, 0x6a, 0x0f, 0x79, 0xcc, 0xd3, 0x3a, 0xa4, 0x6c, 0x4f, 0x69, 0x10, 0xb3, 0xf1, 0x44, 0x38, 0x51, 0x98, 0x7e, - 0x6c, 0xd7, 0xeb, 0xb3, 0x94, 0x4d, 0xc3, 0xf4, 0xae, 0x2e, 0x5b, 0x04, 0x9f, 0x37, 0x8e, 0xc2, 0x27, 0xa3, 0xe3, - 0xb6, 0x48, 0xc3, 0x24, 0x63, 0xb0, 0x4d, 0x41, 0x18, 0xc7, 0xce, 0xd1, 0x49, 0x63, 0x9a, 0xed, 0xa9, 0x40, 0x5e, - 0x98, 0x88, 0xfc, 0x0a, 0x7f, 0x04, 0xb8, 0xfd, 0x6b, 0x91, 0xe0, 0xeb, 0xb9, 0x10, 0x3c, 0x59, 0x0e, 0xe7, 0x69, - 0xc6, 0xd3, 0x60, 0xc6, 0x59, 0x22, 0x68, 0xda, 0xbe, 0xe6, 0x69, 0x44, 0xd3, 0x7a, 0x1a, 0x46, 0x6c, 0x9e, 0x05, - 0xc7, 0xb3, 0xdb, 0x36, 0x68, 0x16, 0xe3, 0x94, 0xcf, 0x93, 0x48, 0xcf, 0xc5, 0x92, 0x09, 0x4d, 0x99, 0xb0, 0x2b, - 0xe4, 0x2b, 0x4c, 0x82, 0x98, 0x25, 0x34, 0x4c, 0xeb, 0x63, 0xe8, 0x0c, 0x66, 0x51, 0x23, 0xa2, 0x63, 0x9c, 0x8e, - 0xaf, 0x43, 0xaf, 0xd9, 0x7a, 0x8c, 0xcd, 0xff, 0xfe, 0x09, 0x72, 0x1a, 0xdb, 0x8b, 0x9b, 0x8d, 0xc6, 0x9f, 0x50, - 0x7b, 0x6d, 0x16, 0x09, 0x50, 0xd0, 0x9c, 0xdd, 0x3a, 0x19, 0x87, 0x9c, 0xb6, 0x6d, 0x3d, 0xdb, 0xb3, 0x30, 0x82, - 0x84, 0xe0, 0xa0, 0x35, 0xbb, 0xcd, 0x61, 0x75, 0x81, 0x4a, 0x32, 0xd5, 0x8b, 0xd4, 0x4f, 0xcb, 0xdf, 0x0a, 0xf1, - 0xe9, 0x76, 0x88, 0x5b, 0x06, 0xe2, 0x12, 0xeb, 0xf5, 0x68, 0x9e, 0xca, 0xd8, 0x6a, 0xd0, 0xcc, 0x14, 0x20, 0x13, - 0xbe, 0xa0, 0xa9, 0x81, 0x43, 0x3e, 0xfc, 0x66, 0x30, 0x5a, 0xdb, 0xc1, 0x38, 0xfd, 0x14, 0x18, 0x69, 0x12, 0x2d, - 0xab, 0xfb, 0xda, 0x4c, 0xe9, 0xb4, 0x3d, 0xa1, 0x40, 0x4f, 0x41, 0x0b, 0x7e, 0xdf, 0xb0, 0x48, 0x4c, 0xd4, 0x4f, - 0x49, 0xce, 0x37, 0xaa, 0xee, 0xa4, 0xd1, 0x50, 0xcf, 0x19, 0xfb, 0x85, 0x06, 0x4d, 0x1f, 0x1a, 0xe4, 0x57, 0xf8, - 0x6f, 0xc5, 0x65, 0xde, 0x2a, 0xf7, 0xc4, 0x5f, 0xdb, 0xb7, 0x7c, 0xad, 0x24, 0xc5, 0xf2, 0x46, 0x34, 0x4e, 0x8d, - 0xac, 0x54, 0xc2, 0x07, 0xdc, 0x76, 0xf2, 0x3c, 0x11, 0xd6, 0x2d, 0x6e, 0x71, 0xb2, 0xde, 0xd7, 0x2a, 0xef, 0x22, - 0x80, 0x48, 0x87, 0x95, 0x6c, 0xc8, 0xdb, 0x49, 0x97, 0x34, 0xda, 0x49, 0xbd, 0x8e, 0x3c, 0x4e, 0xd2, 0x7e, 0xa2, - 0xd3, 0xf3, 0x3c, 0xd6, 0xe3, 0xd2, 0xd8, 0xce, 0x50, 0xc0, 0xe1, 0xaa, 0xe9, 0x6a, 0x55, 0x86, 0x01, 0x98, 0xbc, - 0xae, 0xf1, 0x37, 0xa1, 0x1b, 0xe0, 0xcc, 0xe2, 0xe4, 0x89, 0x79, 0xb1, 0x4b, 0x6a, 0x78, 0x45, 0xcc, 0x87, 0x12, - 0x73, 0xfe, 0x2c, 0x14, 0x13, 0xf0, 0x52, 0x14, 0xe2, 0xa7, 0x4c, 0x62, 0x72, 0x0f, 0x5d, 0xd4, 0x4b, 0x8b, 0x0c, - 0x37, 0xc8, 0xe4, 0x4b, 0x73, 0x18, 0xe5, 0x5b, 0x41, 0x60, 0x44, 0xfc, 0x15, 0x51, 0x36, 0x9d, 0xb1, 0xe8, 0xf6, - 0x1f, 0x6a, 0xd1, 0xd1, 0x44, 0x30, 0x99, 0xbb, 0x6d, 0x22, 0x0e, 0x93, 0x30, 0xbb, 0x1c, 0xaa, 0xbb, 0x92, 0x59, - 0x79, 0x33, 0x20, 0x94, 0xd0, 0x2b, 0x23, 0x8d, 0xa6, 0xd2, 0x1e, 0xfd, 0x41, 0xec, 0xb4, 0x4f, 0xd2, 0xfb, 0xec, - 0x93, 0x62, 0xe1, 0x19, 0x9f, 0xa7, 0x43, 0x08, 0x47, 0x6a, 0xa9, 0xb7, 0xe9, 0xb8, 0x71, 0xa5, 0x8a, 0xe1, 0x62, - 0x61, 0x65, 0x82, 0x0a, 0xcc, 0xec, 0x97, 0x4a, 0x50, 0x19, 0xf2, 0x52, 0xf7, 0x35, 0xb4, 0x88, 0x33, 0x4b, 0x02, - 0x99, 0x1d, 0xc9, 0xa4, 0x46, 0x2f, 0x21, 0xdd, 0xc4, 0x9f, 0x27, 0xec, 0xe7, 0x39, 0xbd, 0x64, 0xa0, 0x6b, 0x32, - 0x9f, 0x45, 0x32, 0xd6, 0x04, 0xb2, 0xaf, 0xde, 0x84, 0xe0, 0x05, 0x8b, 0xd4, 0xc6, 0x24, 0xb2, 0x52, 0xe7, 0x36, - 0xb9, 0x75, 0x17, 0xfc, 0xc5, 0xa0, 0x1d, 0x30, 0x1c, 0xf1, 0x69, 0xc8, 0x92, 0x40, 0xba, 0x7c, 0x8b, 0xc1, 0x02, - 0x68, 0x8d, 0x59, 0x14, 0x24, 0x7a, 0x7b, 0x9a, 0xc8, 0xff, 0xc0, 0x59, 0x22, 0xbb, 0xe6, 0x6d, 0x2e, 0x11, 0xaa, - 0xd0, 0x47, 0x0c, 0x82, 0xcf, 0x94, 0x5c, 0xe3, 0x08, 0xdb, 0xd5, 0xc5, 0xb5, 0xf3, 0xca, 0x0e, 0x34, 0xd6, 0x36, - 0x4a, 0x19, 0x01, 0x7c, 0xbd, 0x34, 0xe3, 0xa9, 0xf0, 0xbc, 0x09, 0x8e, 0x11, 0xe9, 0x4e, 0xa4, 0xb3, 0xab, 0x13, - 0xcb, 0x3f, 0xbd, 0x7a, 0x33, 0x68, 0x16, 0xe6, 0x7b, 0xe5, 0x36, 0xb0, 0x4a, 0x8e, 0xd2, 0x37, 0x4a, 0xe5, 0x32, - 0x8a, 0xdf, 0x6a, 0xa9, 0xe5, 0x73, 0xb1, 0x5c, 0xac, 0x8f, 0x9b, 0x12, 0x55, 0x5e, 0x05, 0x08, 0x19, 0x2c, 0xda, - 0x31, 0x15, 0xca, 0xcb, 0x75, 0x17, 0xaa, 0xe4, 0x95, 0x12, 0xd1, 0x97, 0xfb, 0xcb, 0x54, 0xcf, 0x98, 0x5f, 0x31, - 0xe3, 0x64, 0xaa, 0x92, 0x5c, 0xae, 0x31, 0x62, 0xe9, 0xa1, 0xdb, 0x9a, 0x29, 0x58, 0xee, 0x48, 0xba, 0x95, 0x6e, - 0x7d, 0xf5, 0x48, 0x53, 0x52, 0x86, 0xbb, 0x36, 0x06, 0x80, 0x5c, 0xbd, 0x6d, 0x80, 0x81, 0xd9, 0x9a, 0x09, 0xb3, - 0x04, 0xd0, 0xc6, 0x46, 0x14, 0x2e, 0xd2, 0x5c, 0xed, 0x2f, 0xbf, 0x15, 0xf9, 0xa1, 0xd5, 0x54, 0xfe, 0x66, 0x11, - 0xfc, 0x05, 0x09, 0xb8, 0x54, 0x4a, 0x69, 0xe0, 0x7e, 0xf3, 0xe6, 0xe2, 0x9d, 0x8b, 0xe1, 0xdd, 0x5c, 0x34, 0xcd, - 0x82, 0xa5, 0xab, 0x53, 0xe3, 0xea, 0x10, 0x66, 0x75, 0x03, 0x37, 0x9c, 0xc1, 0x55, 0x63, 0xc9, 0x0b, 0x0e, 0x6f, - 0xeb, 0x37, 0x37, 0x37, 0x75, 0xb8, 0x09, 0x55, 0x9f, 0xa7, 0x31, 0x4d, 0x86, 0x3c, 0xa2, 0x91, 0x9b, 0xe7, 0xc8, - 0x17, 0x13, 0x9a, 0x14, 0x6f, 0xef, 0xe1, 0x31, 0xf5, 0x63, 0x3e, 0x56, 0xb7, 0x38, 0xd7, 0xad, 0xea, 0xe1, 0x55, - 0x47, 0xbe, 0x95, 0xaa, 0xdb, 0x11, 0xea, 0x7d, 0x60, 0x22, 0x85, 0x9f, 0x5d, 0x88, 0xb9, 0x74, 0x0e, 0xc5, 0x44, - 0x3e, 0x5c, 0xc0, 0x09, 0x93, 0x4f, 0xfb, 0xcb, 0x0d, 0xea, 0xeb, 0xc1, 0x10, 0x93, 0xae, 0x5a, 0x73, 0x26, 0x5b, - 0x5d, 0x05, 0xc3, 0xab, 0xab, 0xbc, 0x73, 0x08, 0x63, 0x1d, 0x9a, 0x71, 0xaf, 0x79, 0x74, 0x67, 0xfa, 0x17, 0x14, - 0x09, 0x6f, 0x27, 0x4a, 0x49, 0x17, 0x86, 0x80, 0x79, 0xa3, 0x2e, 0x60, 0x05, 0x28, 0x12, 0x7a, 0x47, 0x45, 0x89, - 0x3c, 0xe2, 0xaa, 0x68, 0x17, 0x04, 0xaa, 0x61, 0x79, 0x50, 0x94, 0xfb, 0xb5, 0x24, 0x08, 0x03, 0x52, 0x64, 0x43, - 0x77, 0x85, 0xe0, 0xaf, 0x84, 0xac, 0x73, 0xa8, 0xf0, 0x70, 0x65, 0xbf, 0x0b, 0x45, 0xbd, 0xa7, 0xa0, 0xc0, 0x56, - 0x3f, 0x13, 0xf8, 0xa3, 0xc0, 0x1f, 0xaf, 0x64, 0x53, 0x23, 0xbd, 0x40, 0xad, 0x02, 0x29, 0xdf, 0x30, 0x6a, 0xca, - 0x90, 0xc7, 0x71, 0x38, 0xcb, 0x68, 0x60, 0x7e, 0x68, 0x41, 0x06, 0xf2, 0x70, 0x53, 0x73, 0xd0, 0xf9, 0x38, 0xe7, - 0xa0, 0x5f, 0x6c, 0xaa, 0x35, 0x8b, 0x30, 0xf5, 0xea, 0xf5, 0x61, 0xfd, 0x7a, 0x8c, 0x72, 0x31, 0x59, 0xda, 0x62, - 0xf0, 0x51, 0xa3, 0xd1, 0x86, 0xe4, 0xc9, 0x7a, 0x18, 0xb3, 0x71, 0x12, 0xc4, 0x74, 0x24, 0x72, 0x01, 0xd7, 0xda, - 0x96, 0x46, 0xef, 0xf0, 0x5b, 0x27, 0x29, 0x9d, 0x3a, 0x3e, 0xfc, 0x7b, 0xff, 0xc4, 0xb9, 0x88, 0x82, 0x44, 0x4c, - 0xea, 0x32, 0x4d, 0x17, 0x2e, 0x19, 0x88, 0x49, 0xe5, 0x79, 0x69, 0x4d, 0x34, 0xa4, 0xa0, 0x93, 0xe5, 0x22, 0x75, - 0xc4, 0x04, 0x8b, 0xd4, 0x6e, 0x97, 0xa0, 0xe5, 0xc6, 0x0a, 0x36, 0x55, 0x83, 0x23, 0x94, 0x67, 0x52, 0x93, 0xde, - 0x6c, 0x6c, 0xf4, 0xab, 0xea, 0xd3, 0x06, 0xfa, 0x2c, 0x4d, 0x30, 0x57, 0x9e, 0xe8, 0xa5, 0xea, 0xf1, 0x10, 0x64, - 0x56, 0x74, 0x54, 0x6c, 0xf7, 0x40, 0x39, 0x4b, 0x66, 0x73, 0xd1, 0x97, 0x5e, 0xf0, 0x14, 0x6e, 0x54, 0x0c, 0xb0, - 0x55, 0x02, 0x38, 0x18, 0x2c, 0x15, 0x30, 0xc3, 0x30, 0x1e, 0x7a, 0x00, 0x91, 0x53, 0x77, 0x4e, 0x53, 0x3a, 0x45, - 0xed, 0x29, 0x4b, 0xea, 0xaa, 0xee, 0xc4, 0xd2, 0x63, 0xfc, 0xc7, 0xf0, 0x94, 0xfb, 0x72, 0x34, 0x2c, 0x93, 0x5d, - 0xb7, 0xe0, 0xf2, 0x6a, 0x90, 0xe7, 0xed, 0x54, 0x78, 0xfd, 0xa7, 0x1e, 0x1a, 0xe0, 0xaf, 0xac, 0xb7, 0xb9, 0xb8, - 0xe6, 0xa8, 0xb8, 0xb8, 0x85, 0x76, 0x34, 0xb1, 0xcf, 0x82, 0x6c, 0xf6, 0x15, 0x81, 0x86, 0x2f, 0x3c, 0x97, 0x66, - 0xb3, 0xba, 0x62, 0x76, 0x75, 0x49, 0xb2, 0x2e, 0x74, 0x45, 0xda, 0xb5, 0xfb, 0x83, 0x58, 0x4a, 0x3e, 0xa6, 0x6f, - 0x75, 0x28, 0xef, 0xc3, 0xa0, 0xb8, 0x05, 0xa4, 0x9f, 0xed, 0x7b, 0x3f, 0xa8, 0xc2, 0x4f, 0xae, 0xce, 0xaa, 0x4c, - 0x11, 0x18, 0x59, 0xf1, 0xc6, 0xbb, 0x30, 0x8e, 0x61, 0xc2, 0x2b, 0xa3, 0xef, 0xd8, 0x6f, 0x09, 0xe9, 0x8b, 0x81, - 0x87, 0x72, 0x7d, 0x4e, 0x9f, 0x4a, 0x1d, 0xd4, 0x7a, 0xcd, 0xde, 0x9e, 0x30, 0xd1, 0x25, 0x25, 0xae, 0x19, 0xc4, - 0xc7, 0x2b, 0x89, 0xd4, 0xed, 0x92, 0x77, 0x29, 0x0d, 0xd6, 0x91, 0x0b, 0x22, 0x6e, 0x9a, 0x44, 0xae, 0xf3, 0x97, - 0x61, 0xcc, 0x86, 0x1f, 0x89, 0xbb, 0xbf, 0xf4, 0xd0, 0xe6, 0x3d, 0x49, 0xc9, 0x15, 0x0c, 0x87, 0x47, 0x55, 0xcf, - 0x7b, 0xe2, 0x5b, 0xcc, 0x5b, 0xbd, 0x46, 0xc7, 0xed, 0xee, 0x2f, 0x81, 0xf1, 0xa8, 0x79, 0xba, 0x57, 0xf9, 0x65, - 0xf9, 0x72, 0xac, 0x12, 0x0a, 0x40, 0xb3, 0x2a, 0x77, 0x24, 0x51, 0x11, 0xf7, 0x93, 0x94, 0xe6, 0x3a, 0x8a, 0xa9, - 0x01, 0x9c, 0x42, 0xf3, 0x37, 0xd7, 0xf9, 0x4b, 0x51, 0x46, 0x0b, 0x17, 0x88, 0xcc, 0xe1, 0x20, 0x2e, 0xcc, 0x05, - 0x76, 0xaf, 0x1f, 0x51, 0x11, 0xb2, 0x58, 0x75, 0x69, 0x1b, 0x8b, 0x7d, 0x6d, 0x45, 0xab, 0x55, 0x56, 0x5d, 0x0b, - 0xab, 0x62, 0x50, 0xae, 0xac, 0x73, 0x58, 0xc2, 0x2d, 0x57, 0x26, 0xcf, 0xa4, 0x1d, 0x4b, 0x2c, 0x57, 0xa8, 0xea, - 0x9c, 0xbf, 0x0c, 0xe5, 0x3d, 0x23, 0x00, 0x90, 0x6b, 0x00, 0x21, 0xca, 0xad, 0xee, 0xd1, 0x78, 0x31, 0xe1, 0xbe, - 0x08, 0xd3, 0x31, 0x15, 0x6b, 0x88, 0x8d, 0x55, 0x52, 0x6b, 0xdb, 0x44, 0xb4, 0x37, 0xa0, 0x0d, 0xab, 0xd0, 0x5e, - 0x01, 0xd2, 0x7b, 0xfb, 0x4b, 0x96, 0x93, 0xfd, 0xa5, 0x92, 0x6b, 0xef, 0xdf, 0x7e, 0x05, 0xb7, 0x22, 0x79, 0x02, - 0x96, 0xc8, 0x04, 0x81, 0xa4, 0x95, 0x9b, 0xa3, 0x44, 0x08, 0x97, 0x22, 0x44, 0x71, 0x02, 0x47, 0xce, 0x25, 0x41, - 0xcc, 0x5d, 0xa7, 0xa7, 0x20, 0xa7, 0x91, 0x82, 0x99, 0x24, 0xb2, 0x17, 0xcf, 0x3b, 0x87, 0xaa, 0xb5, 0x12, 0x01, - 0xaa, 0x11, 0x20, 0x41, 0x9e, 0xd3, 0x12, 0x07, 0x90, 0x08, 0x6d, 0xe3, 0x21, 0x62, 0x8b, 0x82, 0xd8, 0xe4, 0x8d, - 0xab, 0x6e, 0x27, 0x0e, 0xaf, 0x69, 0xdc, 0xdd, 0x5f, 0x26, 0xab, 0x55, 0x23, 0xef, 0x1c, 0xaa, 0x47, 0xa7, 0x23, - 0xf9, 0x86, 0x7a, 0x43, 0xa6, 0xdc, 0x62, 0xb8, 0xc6, 0x08, 0xe9, 0xa1, 0x26, 0x2f, 0x2a, 0xd0, 0x03, 0xe4, 0xae, - 0x23, 0x33, 0x32, 0x64, 0xa3, 0x42, 0x83, 0xca, 0x5d, 0x87, 0x45, 0x9b, 0x65, 0x99, 0xa0, 0x33, 0x28, 0x9d, 0xac, - 0x56, 0xcd, 0xdc, 0x75, 0xa6, 0x2c, 0x81, 0xa7, 0x64, 0xb5, 0x92, 0x37, 0x04, 0xa7, 0x2c, 0xf1, 0x1a, 0x40, 0xb6, - 0xae, 0x33, 0x0d, 0x6f, 0xe5, 0x82, 0x4d, 0x4d, 0x78, 0xeb, 0x35, 0x75, 0x95, 0x5f, 0xe0, 0x27, 0x03, 0x8a, 0x2b, - 0x77, 0x34, 0xd6, 0x3b, 0x1a, 0xe1, 0xb9, 0xba, 0xfb, 0x44, 0xbc, 0x88, 0xc4, 0xdb, 0x77, 0x34, 0x32, 0x3b, 0x3a, - 0xdf, 0xb1, 0xa3, 0xf3, 0x7b, 0x76, 0x34, 0xd4, 0xbb, 0xe7, 0x14, 0xb8, 0xe3, 0xab, 0x55, 0xb3, 0x51, 0x62, 0xaf, - 0x73, 0x18, 0xb1, 0x05, 0xec, 0x06, 0xe8, 0x85, 0x82, 0x4d, 0xe9, 0x76, 0xa2, 0xac, 0xa2, 0x98, 0xfe, 0x2a, 0x4c, - 0x96, 0x58, 0x48, 0xaa, 0x58, 0xb0, 0xe9, 0xba, 0x08, 0xd2, 0xfd, 0x91, 0x94, 0xcd, 0x00, 0x0f, 0x19, 0xe0, 0x61, - 0x62, 0xde, 0x98, 0xe9, 0xb9, 0xef, 0x5c, 0xec, 0x3a, 0xae, 0x21, 0xeb, 0xab, 0xfc, 0x12, 0x64, 0x84, 0x5c, 0xdf, - 0x83, 0x68, 0x11, 0x5a, 0xbb, 0xdd, 0xdd, 0x34, 0x07, 0xf1, 0xf4, 0x1b, 0x9e, 0x46, 0x6e, 0xa0, 0x9a, 0xfe, 0x2a, - 0x54, 0x4d, 0x59, 0xa2, 0xb3, 0xb3, 0x76, 0xd2, 0x5a, 0x59, 0x6f, 0x53, 0x5c, 0xeb, 0xe4, 0x44, 0xb5, 0x98, 0x85, - 0x42, 0xd0, 0x34, 0xd1, 0x94, 0xeb, 0xba, 0xff, 0x5f, 0x50, 0xe1, 0x16, 0xbe, 0x12, 0x9a, 0x0d, 0x30, 0x04, 0xa8, - 0x35, 0x7c, 0xcd, 0xf3, 0x95, 0x78, 0xda, 0x2b, 0x35, 0xd8, 0x3b, 0x64, 0x5b, 0x19, 0xaa, 0x08, 0x8c, 0x9e, 0xf9, - 0x94, 0x46, 0x97, 0x92, 0x41, 0xf7, 0x86, 0x57, 0x5a, 0x61, 0x5d, 0x13, 0x77, 0x65, 0x07, 0xec, 0xfe, 0x34, 0x6f, - 0x3d, 0x3e, 0x3e, 0x77, 0xb1, 0xe2, 0xf1, 0x7c, 0x34, 0x72, 0x51, 0xee, 0x3c, 0xac, 0x5b, 0xf3, 0xf8, 0xa7, 0xf9, - 0x97, 0xcf, 0x1b, 0x5f, 0x16, 0x9d, 0x13, 0x20, 0x22, 0x9d, 0x10, 0x60, 0x44, 0x95, 0x05, 0xaf, 0x59, 0xd1, 0x28, - 0x4c, 0x76, 0x2f, 0xa7, 0x6f, 0x2f, 0x27, 0x9b, 0x51, 0x1a, 0x01, 0x71, 0xe2, 0x8d, 0xd2, 0xcb, 0x98, 0x2e, 0xa8, - 0x79, 0x55, 0xe1, 0x96, 0xc9, 0xb6, 0xf4, 0x18, 0xf2, 0x79, 0x22, 0x74, 0x66, 0x84, 0x66, 0xb5, 0xd6, 0x92, 0xae, - 0xe4, 0x1a, 0x6c, 0x1b, 0xe1, 0x4e, 0xc9, 0xb9, 0xaa, 0xf4, 0xca, 0xaf, 0xb0, 0x6b, 0x01, 0xb0, 0x13, 0xb2, 0xde, - 0x8e, 0xf2, 0xa0, 0x81, 0x1b, 0xbb, 0x60, 0xc3, 0x4d, 0x14, 0xb8, 0xee, 0xc0, 0xe0, 0x49, 0x3a, 0x37, 0x2b, 0x6f, - 0x98, 0xd8, 0x89, 0xaf, 0x4f, 0x62, 0xe0, 0x3a, 0x85, 0xc1, 0x12, 0x9a, 0x65, 0x3b, 0x11, 0x50, 0x6c, 0x22, 0x76, - 0xcb, 0xd6, 0xee, 0x8e, 0x51, 0x70, 0x03, 0xc3, 0x09, 0x93, 0x00, 0x17, 0x21, 0x56, 0xdd, 0x8a, 0x8e, 0x46, 0x74, - 0x58, 0xf8, 0x86, 0x21, 0x58, 0x36, 0x62, 0xb1, 0x80, 0x98, 0x91, 0x0c, 0xe6, 0xb8, 0xaf, 0x79, 0x42, 0x5d, 0x64, - 0xd2, 0x3f, 0x35, 0xfc, 0x5a, 0xfe, 0x6f, 0x87, 0x47, 0x8d, 0x58, 0x85, 0x45, 0xcf, 0xb2, 0x5a, 0x19, 0xbf, 0x50, - 0xa5, 0xbc, 0x8a, 0x48, 0x2e, 0x1d, 0x3f, 0xbb, 0x0e, 0xd0, 0xc3, 0x8e, 0xc9, 0xb2, 0xf9, 0xe5, 0x49, 0xb3, 0x91, - 0xbb, 0xd8, 0x85, 0xe1, 0x1e, 0x7a, 0x4a, 0x64, 0xaf, 0x23, 0xe8, 0x35, 0x4f, 0x7e, 0x4d, 0xbf, 0x56, 0xf3, 0x49, - 0xd3, 0xc5, 0xea, 0xcd, 0x03, 0x28, 0x2f, 0x98, 0xc1, 0x10, 0xbc, 0xa5, 0xbf, 0x7b, 0x29, 0xd5, 0xc1, 0x1f, 0x06, - 0xcf, 0xa3, 0x66, 0xc3, 0xc5, 0x6e, 0x26, 0xf8, 0xec, 0x57, 0x2c, 0xe1, 0xc8, 0xc5, 0xee, 0x30, 0xe6, 0x19, 0xb5, - 0xd7, 0xa0, 0xd4, 0xd9, 0xdf, 0xbf, 0x08, 0x05, 0xd1, 0x2c, 0xa5, 0x59, 0xe6, 0xd8, 0xe3, 0x6b, 0x52, 0xfa, 0x04, - 0xc3, 0xdc, 0x4a, 0x71, 0x19, 0x15, 0x12, 0x2f, 0xea, 0xa5, 0x00, 0x36, 0x55, 0xa9, 0xb2, 0x0d, 0x62, 0x93, 0x22, - 0xa0, 0x60, 0x6c, 0x4a, 0xbb, 0xfa, 0xe4, 0xcc, 0x5b, 0x8e, 0x9e, 0x9a, 0x58, 0x05, 0x91, 0x37, 0x27, 0xa8, 0x94, - 0x4c, 0x59, 0x72, 0xb9, 0xa5, 0x34, 0xbc, 0xdd, 0x52, 0x0a, 0x2a, 0x5b, 0x01, 0x9d, 0x7e, 0x5f, 0xcd, 0xa7, 0xb1, - 0x5e, 0x2a, 0x3e, 0x36, 0x88, 0x91, 0x74, 0x74, 0x7e, 0x02, 0x52, 0x6b, 0x1b, 0xe4, 0x08, 0xbf, 0x7d, 0x3a, 0x28, - 0xf9, 0x35, 0xd3, 0x15, 0xa3, 0xfc, 0xbe, 0x15, 0x42, 0x69, 0x1d, 0x1c, 0xde, 0xf1, 0xaf, 0x5a, 0x2b, 0xbd, 0xfd, - 0x34, 0xc1, 0x59, 0x5a, 0xd5, 0xef, 0xd8, 0x7a, 0x7d, 0xf1, 0x7d, 0x7d, 0xef, 0xb7, 0x14, 0x6b, 0xc5, 0xa7, 0xd8, - 0xff, 0x61, 0xcc, 0xa6, 0x25, 0x09, 0x6c, 0x82, 0x29, 0x35, 0x1e, 0xc8, 0x7e, 0xb2, 0x07, 0x51, 0xaa, 0xcf, 0x25, - 0xdc, 0xe9, 0x84, 0x17, 0x67, 0xcc, 0x53, 0x7a, 0x19, 0xf3, 0x9b, 0xf5, 0x17, 0x81, 0xed, 0x6e, 0x3c, 0x61, 0xe3, - 0x89, 0x75, 0x51, 0x8b, 0x92, 0x62, 0x13, 0xee, 0x9d, 0x20, 0xff, 0x97, 0x7f, 0xf6, 0xfd, 0x7f, 0xf9, 0xe7, 0x4f, - 0x36, 0x85, 0xe1, 0xf3, 0x2b, 0x2c, 0xca, 0x61, 0x77, 0x9f, 0xae, 0xed, 0x33, 0x55, 0x71, 0xbe, 0xbd, 0xcd, 0xc6, - 0x26, 0x40, 0xfd, 0xc6, 0x16, 0x6c, 0x14, 0xaa, 0xd3, 0xe7, 0xfc, 0x16, 0xc0, 0x60, 0x5d, 0x9f, 0x84, 0x0c, 0x1a, - 0xfd, 0x2e, 0xd0, 0xae, 0x50, 0xf0, 0xa0, 0x1d, 0xf9, 0xed, 0x18, 0xfe, 0xd4, 0x1a, 0x7e, 0x27, 0xf8, 0xda, 0x3f, - 0x31, 0xbc, 0xba, 0x2a, 0x32, 0xf2, 0xec, 0xae, 0x70, 0xe3, 0xbf, 0xb7, 0x51, 0xa2, 0x15, 0x8f, 0xa0, 0x81, 0xba, - 0xf2, 0x3e, 0x21, 0x19, 0x5e, 0xbd, 0x82, 0xd7, 0xfc, 0x74, 0xae, 0x53, 0xe3, 0xe0, 0xbd, 0x47, 0x38, 0xc0, 0x10, - 0xd5, 0x55, 0xc9, 0x41, 0x37, 0x24, 0x03, 0x94, 0x82, 0xb9, 0x01, 0x60, 0xe2, 0xe1, 0x95, 0xb6, 0x36, 0xcf, 0x95, - 0x1b, 0x26, 0x58, 0x27, 0x6d, 0xed, 0x9e, 0xa9, 0x20, 0x1d, 0x3b, 0xef, 0x24, 0xbe, 0x64, 0x63, 0x5a, 0x5a, 0xf7, - 0xd2, 0xd5, 0x05, 0x76, 0x44, 0xc1, 0x7e, 0x16, 0x61, 0xbc, 0x78, 0x18, 0xe3, 0xdb, 0x2d, 0x50, 0x57, 0xce, 0xea, - 0xdf, 0x5a, 0x25, 0x58, 0xd5, 0x57, 0x15, 0x7d, 0x40, 0x66, 0x25, 0xfc, 0x75, 0x57, 0xe0, 0xf4, 0xef, 0x9f, 0x0e, - 0x9c, 0xf2, 0x07, 0x05, 0x4e, 0xff, 0xfe, 0x87, 0x07, 0x4e, 0xff, 0x6a, 0x07, 0x4e, 0x81, 0x04, 0x7f, 0x7e, 0x50, - 0x70, 0xd3, 0x04, 0x9e, 0xf8, 0x4d, 0x46, 0x9a, 0xda, 0x08, 0x88, 0xf9, 0x18, 0x22, 0x9b, 0xff, 0xf6, 0x81, 0xca, - 0x98, 0x8f, 0xed, 0x30, 0x25, 0xbc, 0xa4, 0x16, 0xe2, 0x92, 0x6d, 0x13, 0x50, 0xd4, 0xa1, 0xc1, 0x46, 0x71, 0x01, - 0xa7, 0x7e, 0x6c, 0x5e, 0x18, 0xe1, 0x06, 0xc5, 0x4b, 0x9f, 0x1a, 0xb8, 0x65, 0x82, 0x87, 0x81, 0x8c, 0x3b, 0x16, - 0x1d, 0x5b, 0x35, 0x73, 0xbb, 0xc4, 0x1e, 0xa1, 0x6d, 0x5e, 0x6a, 0xa3, 0x5e, 0x36, 0xb0, 0x74, 0x7f, 0xba, 0x6d, - 0x3e, 0xed, 0x37, 0xdb, 0x47, 0xcd, 0xa9, 0x1b, 0xb8, 0x20, 0xe0, 0x65, 0x41, 0xa3, 0x7d, 0x74, 0x04, 0x05, 0x37, - 0x56, 0x41, 0x0b, 0x0a, 0x98, 0x55, 0x70, 0x02, 0x05, 0x43, 0xab, 0xe0, 0x11, 0x14, 0x44, 0x56, 0xc1, 0x63, 0x28, - 0x58, 0xb8, 0x79, 0x9f, 0x15, 0xe0, 0x3e, 0x46, 0x03, 0xac, 0xec, 0x2e, 0x53, 0xf6, 0x18, 0x37, 0x21, 0x62, 0x19, - 0x8e, 0x65, 0xa2, 0x15, 0xb8, 0x33, 0x03, 0x8e, 0x6f, 0x26, 0x34, 0x09, 0x20, 0x66, 0xfc, 0x4c, 0x4a, 0x48, 0x5f, - 0xf0, 0x77, 0x6c, 0x4a, 0xcd, 0xe7, 0x41, 0x0c, 0x1e, 0x1c, 0x17, 0xf5, 0x1b, 0x83, 0xbc, 0x5d, 0xec, 0x9c, 0x0a, - 0x75, 0xea, 0xa4, 0x1b, 0xb5, 0x97, 0x65, 0x9d, 0x9a, 0xae, 0x5e, 0xec, 0xf9, 0x8e, 0x08, 0x98, 0xe5, 0x49, 0x19, - 0xc5, 0xfc, 0xa6, 0x7e, 0xeb, 0x76, 0xb7, 0x47, 0xc5, 0x00, 0xa2, 0x22, 0x2a, 0x26, 0xd7, 0x54, 0x3c, 0xbd, 0x0b, - 0xc7, 0xc5, 0xef, 0x57, 0x34, 0xcb, 0xc2, 0xb1, 0x6e, 0xb9, 0x3b, 0x0a, 0x26, 0x41, 0xb4, 0x23, 0x60, 0x06, 0x08, - 0x88, 0x64, 0xc1, 0x66, 0x81, 0x27, 0x42, 0x07, 0xb6, 0x00, 0x3b, 0xd5, 0x98, 0x98, 0x9c, 0xbe, 0x5a, 0x24, 0xc2, - 0x71, 0x59, 0xd0, 0x99, 0xa5, 0x54, 0x96, 0x2a, 0x0c, 0xe7, 0x9d, 0x43, 0x28, 0x50, 0xd5, 0x3b, 0x62, 0x5f, 0xc6, - 0xed, 0xb1, 0x3b, 0x02, 0xe6, 0x98, 0xd8, 0x97, 0x9d, 0x5e, 0x54, 0xe4, 0x16, 0x6d, 0x46, 0x5c, 0x3e, 0x6f, 0x0e, - 0xe1, 0x3f, 0x1d, 0xcf, 0xf9, 0x7c, 0x34, 0x1a, 0xdd, 0x1b, 0x0b, 0xfb, 0x3c, 0x1a, 0xd1, 0x16, 0x3d, 0x69, 0x43, - 0xea, 0x49, 0x5d, 0x47, 0x50, 0x9a, 0xb9, 0xc4, 0xdd, 0xf2, 0x61, 0x8d, 0x21, 0xd8, 0x22, 0x26, 0xcb, 0x87, 0xc7, - 0xc5, 0xf2, 0x59, 0x4a, 0x97, 0xd3, 0x30, 0x1d, 0xb3, 0x24, 0x68, 0xe4, 0xfe, 0x42, 0x07, 0x92, 0x3e, 0x3f, 0x3d, - 0x3d, 0xcd, 0xfd, 0xc8, 0x3c, 0x35, 0xa2, 0x28, 0xf7, 0x87, 0xcb, 0x62, 0x19, 0x8d, 0xc6, 0x68, 0x94, 0xfb, 0xcc, - 0x14, 0x1c, 0xb5, 0x86, 0xd1, 0x51, 0x2b, 0xf7, 0x6f, 0xac, 0x16, 0xb9, 0x4f, 0xf5, 0x53, 0x4a, 0xa3, 0x4a, 0xfe, - 0xca, 0xe3, 0x46, 0x23, 0xf7, 0x15, 0xa1, 0x2d, 0xc1, 0x98, 0x54, 0x3f, 0x83, 0x70, 0x2e, 0x38, 0xb0, 0xe4, 0x36, - 0x17, 0x5e, 0xff, 0x52, 0xbf, 0x1b, 0x44, 0x7d, 0x47, 0x23, 0x47, 0x03, 0xfc, 0xb3, 0x1d, 0xf2, 0x01, 0x62, 0x96, - 0xa1, 0x1e, 0x6e, 0x22, 0x42, 0x95, 0x6a, 0xa0, 0x2c, 0x59, 0xfd, 0x33, 0xe1, 0x65, 0x24, 0x08, 0xf8, 0x0f, 0xb4, - 0x54, 0x2f, 0xb1, 0x13, 0x74, 0x07, 0xd7, 0xa7, 0xf4, 0x93, 0x5c, 0xff, 0xee, 0x21, 0x4c, 0x9f, 0xd2, 0x3f, 0x9a, - 0xe9, 0xeb, 0x37, 0xbd, 0x2a, 0xa6, 0xaf, 0xd8, 0xda, 0x54, 0x10, 0x77, 0x38, 0xa1, 0xc3, 0x8f, 0xd7, 0xfc, 0xb6, - 0x0e, 0x47, 0x22, 0x75, 0x25, 0x3f, 0x1d, 0xfd, 0xd6, 0x5c, 0x17, 0x33, 0x98, 0xf5, 0x19, 0x0e, 0x29, 0xf4, 0xdf, - 0x24, 0xc4, 0x7d, 0x63, 0x2c, 0x52, 0x55, 0x32, 0x1a, 0x11, 0xf7, 0xcd, 0x68, 0xe4, 0x9a, 0x1b, 0x8e, 0xa1, 0xa0, - 0xb2, 0xd5, 0xeb, 0x4a, 0x89, 0x6c, 0xf5, 0xe5, 0x97, 0x76, 0x99, 0x5d, 0xa0, 0x03, 0x46, 0x76, 0x70, 0x48, 0xd7, - 0x44, 0x2c, 0x83, 0xa3, 0x06, 0x5f, 0x07, 0xa9, 0xbe, 0x62, 0x31, 0xad, 0xbc, 0x0d, 0xbb, 0x00, 0x78, 0xcb, 0x2b, - 0xbc, 0xd7, 0xaf, 0xf7, 0x8f, 0xa9, 0xc9, 0x36, 0x7c, 0x7a, 0xf7, 0x55, 0xe4, 0x4d, 0x05, 0xca, 0x59, 0xf6, 0x26, - 0x59, 0xbb, 0xba, 0xa3, 0x60, 0x24, 0xc4, 0x5e, 0x56, 0x2e, 0xf8, 0x78, 0x1c, 0xc3, 0xf7, 0x59, 0x96, 0x95, 0xd7, - 0xbe, 0xaa, 0xee, 0xbd, 0xca, 0x7a, 0x03, 0xbb, 0xa3, 0x7e, 0x49, 0xaa, 0xfc, 0x5c, 0x94, 0x4a, 0xf9, 0x5e, 0xe8, - 0xef, 0x06, 0x49, 0x63, 0x76, 0xa9, 0xf9, 0xff, 0x52, 0x25, 0x0a, 0x0b, 0x48, 0x92, 0x51, 0x03, 0x47, 0x79, 0xae, - 0xaf, 0x58, 0x44, 0x2c, 0x9b, 0xc1, 0xeb, 0x48, 0x55, 0x4f, 0xfa, 0x29, 0x16, 0x9e, 0xdd, 0x58, 0x51, 0x99, 0xca, - 0x76, 0xe5, 0x26, 0x2c, 0xa3, 0xdc, 0xdc, 0x53, 0x91, 0xbb, 0xda, 0x5b, 0x6e, 0x90, 0xe8, 0x3a, 0x0a, 0x9f, 0x2a, - 0x5e, 0x64, 0xad, 0x10, 0x5c, 0xd6, 0xc5, 0x86, 0x98, 0x2a, 0x53, 0x90, 0xdb, 0x51, 0x47, 0x59, 0xa3, 0xb0, 0x25, - 0x63, 0x1c, 0xd9, 0x2c, 0x4c, 0x14, 0x1b, 0x25, 0xae, 0xe2, 0x07, 0xfb, 0xcb, 0x72, 0xe7, 0x73, 0xd7, 0x80, 0xad, - 0x88, 0xb7, 0xdb, 0x39, 0x84, 0x0e, 0x5d, 0xa7, 0x02, 0x7a, 0xb2, 0x11, 0x1a, 0xf9, 0x44, 0x92, 0xc2, 0x95, 0x9f, - 0xdd, 0x60, 0x3f, 0xbb, 0x71, 0xfe, 0xbc, 0xac, 0xdf, 0xd0, 0xeb, 0x8f, 0x4c, 0xd4, 0x45, 0x38, 0xab, 0x83, 0xb9, - 0x22, 0x5d, 0x9a, 0x9a, 0x3d, 0xcb, 0xdc, 0x3c, 0xf5, 0x82, 0x82, 0xf6, 0x3c, 0x83, 0x5c, 0x06, 0xa9, 0x74, 0x07, - 0x09, 0x4f, 0x68, 0xbb, 0x9a, 0x83, 0x69, 0x87, 0xc6, 0x0d, 0xb6, 0x06, 0x4b, 0x0e, 0xb9, 0x0f, 0xe2, 0x2e, 0x68, - 0x68, 0xb6, 0xde, 0x30, 0x71, 0xef, 0xc6, 0xd6, 0xf6, 0x81, 0x46, 0x6e, 0x4d, 0x4a, 0xaf, 0x74, 0x33, 0xfe, 0xbf, - 0x2b, 0x7e, 0xff, 0xa9, 0x8c, 0x44, 0x70, 0x84, 0x9a, 0xff, 0xad, 0x54, 0xce, 0xf5, 0x62, 0x99, 0x91, 0xf8, 0x10, - 0xc8, 0x82, 0x70, 0x24, 0x68, 0x8a, 0x1f, 0xd2, 0xf2, 0x5a, 0x5e, 0x1e, 0x5a, 0x82, 0x98, 0x09, 0x9a, 0xa7, 0xb3, - 0xdb, 0x87, 0x0f, 0x7f, 0xff, 0xf2, 0x73, 0x8d, 0x23, 0xf3, 0x32, 0x1d, 0xd7, 0x6d, 0xc3, 0x41, 0x88, 0xc3, 0xbb, - 0x80, 0x25, 0x52, 0xe6, 0x5d, 0x83, 0x37, 0xb3, 0x3d, 0xe3, 0x3a, 0xb5, 0x36, 0xa5, 0xb1, 0xfc, 0x72, 0x9e, 0xde, - 0x8a, 0xa3, 0x47, 0xb3, 0x5b, 0xb3, 0x1b, 0xcd, 0xb5, 0x94, 0xd9, 0x3f, 0x34, 0x33, 0x76, 0x77, 0x2a, 0x6e, 0x35, - 0xbb, 0xf3, 0x64, 0x76, 0xdb, 0x56, 0x82, 0xb6, 0x9e, 0x2a, 0xa8, 0x1a, 0xb3, 0x5b, 0x3b, 0x37, 0xb8, 0x1c, 0xc8, - 0xf1, 0x8f, 0x32, 0x87, 0x86, 0x19, 0x6d, 0xc3, 0xeb, 0xc2, 0xd9, 0x30, 0x8c, 0xb5, 0x30, 0x9f, 0xb2, 0x28, 0x8a, - 0x69, 0xdb, 0xc8, 0x6b, 0xa7, 0xf9, 0x08, 0x52, 0x6b, 0xed, 0x2d, 0xab, 0xee, 0x8a, 0x85, 0xbc, 0x02, 0x4f, 0xe1, - 0x75, 0xc6, 0x63, 0xf8, 0x56, 0xc7, 0x56, 0x74, 0xea, 0x9c, 0xd3, 0x46, 0x89, 0x3c, 0xf9, 0xbb, 0xba, 0x96, 0x93, - 0xc6, 0x9f, 0xda, 0x72, 0xc3, 0x1b, 0x6d, 0xc1, 0x67, 0x41, 0xfd, 0xa8, 0xba, 0x10, 0xa8, 0x2a, 0x96, 0x80, 0xb7, - 0x2c, 0x0b, 0x83, 0xb4, 0x52, 0x7c, 0xda, 0xf1, 0x9b, 0xba, 0x4c, 0x0e, 0x00, 0xd9, 0x5c, 0x45, 0x51, 0x5e, 0x5d, - 0xcc, 0xbf, 0xcd, 0x69, 0x79, 0xb2, 0xfd, 0xb4, 0x3c, 0x31, 0xa7, 0xe5, 0x7e, 0x8a, 0xfd, 0x7c, 0xd4, 0x84, 0xff, - 0xda, 0xe5, 0x82, 0x82, 0x86, 0x73, 0x34, 0xbb, 0x75, 0x40, 0x4f, 0xab, 0xb7, 0x66, 0xb7, 0x2a, 0x33, 0x1a, 0x22, - 0x2e, 0x0d, 0xc8, 0x15, 0xc6, 0x0d, 0x07, 0x0a, 0xe1, 0xff, 0x46, 0xa5, 0xaa, 0x79, 0x0c, 0x75, 0xd0, 0xeb, 0x64, - 0xb3, 0xae, 0x75, 0xff, 0xa1, 0x0d, 0x12, 0x2e, 0xbc, 0xc0, 0x70, 0x63, 0xe4, 0x8b, 0xf0, 0xfa, 0x9a, 0x46, 0xc1, - 0x88, 0x0f, 0xe7, 0xd9, 0x3f, 0x69, 0xf8, 0x35, 0x12, 0xef, 0x3d, 0xd2, 0x6b, 0xe3, 0x98, 0xae, 0x2a, 0x4f, 0xdb, - 0x8c, 0xb0, 0x2c, 0xf6, 0x29, 0xc8, 0x86, 0x61, 0x4c, 0xbd, 0x96, 0x7f, 0xbc, 0xe5, 0x10, 0xfc, 0xbb, 0xec, 0xcd, - 0xd6, 0xc5, 0xfc, 0x5e, 0x64, 0xdc, 0x8b, 0x84, 0x5f, 0x85, 0x03, 0x7b, 0x0f, 0x1b, 0xa7, 0xdb, 0xc1, 0xed, 0x9b, - 0x99, 0x06, 0x46, 0x28, 0x68, 0xb9, 0x13, 0xd1, 0x51, 0x38, 0x8f, 0xc5, 0xfd, 0xa3, 0xee, 0xa2, 0x8c, 0x8d, 0x51, - 0xef, 0x61, 0xe8, 0x65, 0xdb, 0x07, 0x72, 0xe9, 0xcf, 0x9f, 0x1c, 0xc3, 0x7f, 0x2a, 0x6b, 0xeb, 0xae, 0xd4, 0xd5, - 0x95, 0xad, 0x0a, 0xba, 0xfa, 0xa8, 0xa2, 0x8c, 0x2b, 0x11, 0x2e, 0xf5, 0xf1, 0x87, 0xb6, 0x06, 0xad, 0xf2, 0x41, - 0xcd, 0xb5, 0x96, 0xf5, 0xab, 0x5a, 0xff, 0xba, 0xc1, 0x1f, 0xd8, 0x76, 0xa8, 0x34, 0xd7, 0x6a, 0x5b, 0xfd, 0xe9, - 0xc0, 0x8d, 0xc6, 0x06, 0xe3, 0xb2, 0xfd, 0x88, 0xdc, 0x15, 0x26, 0x8a, 0x8a, 0xa1, 0x82, 0x95, 0x32, 0x52, 0x56, - 0x0a, 0xa3, 0xe4, 0xaa, 0xd3, 0xbb, 0x9d, 0xc6, 0xce, 0x42, 0x5d, 0x72, 0x24, 0x6e, 0xd3, 0x6f, 0xb8, 0x8e, 0x8c, - 0xde, 0xc3, 0xcb, 0xd6, 0x5d, 0xf9, 0x49, 0x5a, 0xb7, 0x07, 0x9a, 0xd6, 0x62, 0x2c, 0x35, 0xbb, 0x97, 0xe1, 0x1d, - 0x4d, 0x2f, 0x5b, 0xae, 0x03, 0xde, 0x95, 0xba, 0x4a, 0x74, 0x90, 0x65, 0x4e, 0xcb, 0x75, 0x6e, 0xa7, 0x71, 0x92, - 0x11, 0x77, 0x22, 0xc4, 0x2c, 0x50, 0xdf, 0xac, 0xbd, 0x39, 0xf2, 0x79, 0x3a, 0x3e, 0x6c, 0x35, 0x1a, 0x0d, 0x78, - 0x71, 0xab, 0xeb, 0x2c, 0x18, 0xbd, 0x79, 0xca, 0x6f, 0x89, 0xdb, 0x70, 0x1a, 0x4e, 0xb3, 0x75, 0xea, 0x34, 0x5b, - 0xc7, 0xfe, 0xa3, 0x53, 0xb7, 0xfb, 0x99, 0xe3, 0x74, 0x22, 0x3a, 0xca, 0xe0, 0x87, 0xe3, 0x74, 0xa4, 0xe2, 0xa5, - 0x7e, 0x3b, 0x8e, 0x3f, 0x8c, 0xb3, 0x7a, 0xd3, 0x59, 0xea, 0x47, 0xc7, 0x81, 0xab, 0xa0, 0x81, 0xf3, 0xf9, 0xa8, - 0x35, 0x3a, 0x1e, 0x3d, 0x69, 0xeb, 0xe2, 0xfc, 0xb3, 0x4a, 0x73, 0xac, 0xfe, 0xb6, 0xac, 0x6e, 0x99, 0x48, 0xf9, - 0x47, 0xaa, 0x33, 0x09, 0x1d, 0x10, 0x3d, 0x5b, 0xbb, 0xb6, 0x36, 0x67, 0x6a, 0x9e, 0x5e, 0x0f, 0x47, 0xad, 0xb2, - 0xb9, 0x84, 0xf1, 0xb0, 0x00, 0xb2, 0x73, 0x68, 0x40, 0xef, 0xd8, 0x68, 0x6a, 0xd6, 0xb7, 0x21, 0xaa, 0xe9, 0xea, - 0x35, 0x8e, 0xcd, 0xfa, 0x3a, 0x70, 0xf3, 0xc0, 0xe8, 0xaa, 0x12, 0x02, 0xd7, 0x89, 0x88, 0xfb, 0xaa, 0xd9, 0x3a, - 0xc5, 0xcd, 0xe6, 0x23, 0xff, 0xd1, 0xe9, 0xb0, 0x81, 0x8f, 0xfd, 0xe3, 0xfa, 0x91, 0xff, 0x08, 0x9f, 0xd6, 0x4f, - 0xf1, 0xe9, 0x8b, 0xd3, 0x61, 0xfd, 0xd8, 0x3f, 0xc6, 0x8d, 0xfa, 0x29, 0x14, 0xd6, 0x4f, 0xeb, 0xa7, 0x8b, 0xfa, - 0xf1, 0xe9, 0xb0, 0x21, 0x4b, 0x5b, 0xfe, 0xc9, 0x49, 0xbd, 0xd9, 0xf0, 0x4f, 0x4e, 0xf0, 0x89, 0xff, 0xe8, 0x51, - 0xbd, 0x79, 0xe4, 0x3f, 0x7a, 0xf4, 0xf2, 0xe4, 0xd4, 0x3f, 0x82, 0xba, 0xa3, 0xa3, 0xe1, 0x91, 0xdf, 0x6c, 0xd6, - 0xe1, 0x1f, 0x7c, 0xea, 0xb7, 0xd4, 0x8f, 0x66, 0xd3, 0x3f, 0x6a, 0xe2, 0x46, 0x7c, 0xd2, 0xf2, 0x1f, 0x3d, 0xc1, - 0xf2, 0x5f, 0xd9, 0x0c, 0xcb, 0x7f, 0x60, 0x18, 0xfc, 0xc4, 0x6f, 0x3d, 0x52, 0xbf, 0xe4, 0x80, 0x8b, 0xe3, 0xd3, - 0x1f, 0xdd, 0xc3, 0x9d, 0x6b, 0x68, 0xaa, 0x35, 0x9c, 0x9e, 0xf8, 0x47, 0x47, 0xf8, 0xb8, 0xe9, 0x9f, 0x1e, 0x4d, - 0xea, 0xc7, 0x2d, 0xff, 0xd1, 0xe3, 0x61, 0xbd, 0xe9, 0x3f, 0x7e, 0x8c, 0x1b, 0xf5, 0x23, 0xbf, 0x85, 0x9b, 0xfe, - 0xf1, 0x91, 0xfc, 0x71, 0xe4, 0xb7, 0x16, 0x8f, 0x9f, 0xf8, 0x8f, 0x4e, 0x26, 0x8f, 0xfc, 0xe3, 0xef, 0x8e, 0x4f, - 0xfd, 0xd6, 0xd1, 0xe4, 0xe8, 0x91, 0xdf, 0x7a, 0xbc, 0x78, 0xe4, 0x1f, 0x4f, 0xea, 0xad, 0x47, 0xf7, 0xf6, 0x6c, - 0xb6, 0x7c, 0xc0, 0x91, 0xac, 0x86, 0x0a, 0xac, 0x2b, 0xe0, 0xff, 0x89, 0xec, 0xfb, 0xef, 0x38, 0x4c, 0xb6, 0xd9, - 0xf5, 0x89, 0x7f, 0xfa, 0x78, 0xa8, 0x9a, 0x43, 0x41, 0xdd, 0xb4, 0x80, 0x2e, 0x8b, 0xba, 0x9a, 0x56, 0x0e, 0x57, - 0x37, 0x03, 0x99, 0xff, 0xf5, 0x64, 0x8b, 0x3a, 0x4c, 0xac, 0xe6, 0xfd, 0x0f, 0x1d, 0xa7, 0xd8, 0xf2, 0xce, 0xe1, - 0x58, 0x91, 0xfe, 0xb8, 0xfb, 0x99, 0x7a, 0x2b, 0xf3, 0x67, 0x57, 0x38, 0xdb, 0xe5, 0xf8, 0x48, 0x3f, 0xed, 0xf8, - 0x48, 0xe8, 0x43, 0x3c, 0x1f, 0xe9, 0x1f, 0xee, 0xf9, 0xc8, 0xe8, 0x9a, 0xbb, 0xfb, 0x4e, 0x6c, 0x38, 0x38, 0xd6, - 0xad, 0xe2, 0x17, 0xc2, 0xeb, 0x33, 0xf8, 0xfc, 0x57, 0xde, 0xbe, 0x83, 0x37, 0xbf, 0xdb, 0x7e, 0x20, 0x0e, 0x2c, - 0xf6, 0x4e, 0x28, 0x1e, 0xcb, 0x77, 0x21, 0x24, 0xfe, 0x34, 0x42, 0xbe, 0x7b, 0x08, 0x3e, 0xe2, 0x3f, 0x1c, 0x1f, - 0xdc, 0xc6, 0x47, 0xc5, 0x03, 0x2f, 0x3d, 0x0d, 0xd2, 0x53, 0x70, 0x21, 0x9f, 0x3d, 0xb8, 0xfa, 0x54, 0x73, 0x0f, - 0x29, 0x14, 0x65, 0xae, 0x8a, 0x57, 0xbd, 0xfe, 0x35, 0xc1, 0x02, 0x75, 0xcf, 0x91, 0xb8, 0xda, 0x2d, 0x33, 0x93, - 0x52, 0x47, 0x3f, 0x14, 0x42, 0xa9, 0xe5, 0x37, 0xfc, 0x46, 0xe1, 0xd2, 0x81, 0xbb, 0xad, 0x64, 0xc9, 0x45, 0x08, - 0x5f, 0x99, 0x8d, 0xf9, 0x58, 0x7e, 0x8f, 0x16, 0xbe, 0x02, 0x20, 0xbf, 0x0c, 0xac, 0x3e, 0xc0, 0x10, 0xb8, 0xae, - 0x7e, 0x23, 0x06, 0xdc, 0x9d, 0xfc, 0x16, 0xee, 0x97, 0x9a, 0x58, 0xc2, 0x14, 0xbc, 0x1d, 0xaf, 0x68, 0xc4, 0x42, - 0xcf, 0xf5, 0x66, 0x29, 0x1d, 0xd1, 0x34, 0xab, 0x57, 0x2e, 0x5d, 0xca, 0xfb, 0x96, 0xc8, 0x35, 0xdf, 0x33, 0x4d, - 0xe1, 0xad, 0xd6, 0xa4, 0xaf, 0xfd, 0x8d, 0xae, 0x36, 0xc0, 0xdc, 0x1c, 0x9b, 0x92, 0x14, 0x64, 0x6d, 0xa9, 0xb4, - 0xb9, 0x4a, 0x6b, 0x6b, 0xfa, 0xad, 0x13, 0xe4, 0xc8, 0x62, 0x78, 0x5b, 0xf0, 0x0f, 0x5e, 0xfd, 0xa8, 0xf1, 0x27, - 0x64, 0x75, 0x2b, 0x06, 0x1a, 0x68, 0x77, 0x5b, 0x5a, 0x7e, 0x07, 0xba, 0x7a, 0x23, 0xd6, 0x55, 0x14, 0xf1, 0xb9, - 0x5a, 0x3b, 0xbc, 0x77, 0x58, 0xc7, 0xa5, 0xd5, 0x7b, 0x1d, 0x46, 0x6c, 0xec, 0xd9, 0x5f, 0xf9, 0x55, 0x6f, 0x23, - 0x96, 0x1f, 0x07, 0x47, 0x79, 0xd9, 0x24, 0x45, 0x4b, 0x19, 0x25, 0x61, 0x89, 0x93, 0xae, 0x56, 0x5e, 0x0a, 0x2e, - 0x72, 0x62, 0xe1, 0x14, 0x9e, 0x51, 0x05, 0xc9, 0x29, 0x2e, 0x00, 0x92, 0x08, 0x26, 0xa9, 0xfa, 0x5b, 0x16, 0x9b, - 0x1f, 0xda, 0xf1, 0xe5, 0xc7, 0x61, 0x32, 0x06, 0x2a, 0x0c, 0x93, 0xf1, 0x86, 0x5b, 0x4d, 0x05, 0x7a, 0xd6, 0x4a, - 0xcb, 0xa1, 0x4a, 0xf7, 0x59, 0xf6, 0xf4, 0xee, 0x9d, 0x7e, 0x6d, 0x99, 0x0b, 0xde, 0x69, 0x19, 0x95, 0x28, 0x5f, - 0xb1, 0x5c, 0x23, 0x5f, 0x74, 0xa6, 0x54, 0x84, 0x2a, 0xcb, 0x12, 0xf4, 0x09, 0xb8, 0xeb, 0xea, 0x68, 0x6b, 0x94, - 0xb8, 0x52, 0xba, 0x93, 0x88, 0x2e, 0xd8, 0x50, 0x8b, 0x7a, 0xec, 0xe8, 0xfb, 0xfe, 0x75, 0xb9, 0x35, 0xa4, 0x89, - 0x95, 0x3f, 0x66, 0x18, 0xca, 0x3c, 0x7a, 0x92, 0x70, 0xb7, 0xfb, 0x45, 0xf1, 0xd1, 0xd2, 0x5d, 0x9b, 0x10, 0xb3, - 0xe4, 0x63, 0x3f, 0xa5, 0xf1, 0x3f, 0x91, 0x2f, 0xd8, 0x90, 0x27, 0x5f, 0x0c, 0xe0, 0x4b, 0xf2, 0xfe, 0x24, 0xa5, - 0x23, 0xf2, 0x05, 0xc8, 0xf8, 0x40, 0x5a, 0x1f, 0xc0, 0x08, 0x6b, 0xb7, 0xd3, 0x18, 0x4b, 0x8d, 0xe9, 0x01, 0x0a, - 0x91, 0x02, 0xd7, 0x6d, 0x9d, 0xb8, 0x8e, 0xb2, 0x89, 0xe5, 0xef, 0xae, 0x12, 0xa7, 0x52, 0x09, 0x70, 0x9a, 0x2d, - 0xff, 0x64, 0xd2, 0xf2, 0x9f, 0x2c, 0x1e, 0xfb, 0xa7, 0x93, 0xe6, 0xe3, 0x45, 0x1d, 0xfe, 0xb6, 0xfc, 0x27, 0x71, - 0xbd, 0xe5, 0x3f, 0x81, 0xff, 0xbf, 0x3b, 0xf6, 0x4f, 0x26, 0xf5, 0xa6, 0x7f, 0xba, 0x38, 0xf2, 0x8f, 0x5e, 0x36, - 0x5b, 0xfe, 0x91, 0xd3, 0x74, 0x54, 0x3f, 0x60, 0xd7, 0x8a, 0x3b, 0x7f, 0xb1, 0x76, 0x20, 0xb6, 0x04, 0xd1, 0x54, - 0xa6, 0xa8, 0x8b, 0xbd, 0xe2, 0xd3, 0x88, 0xfa, 0x7c, 0x6a, 0x67, 0xdd, 0xb3, 0x30, 0x85, 0xef, 0xd3, 0x54, 0xcf, - 0x6e, 0xa5, 0x0e, 0x57, 0xf8, 0xc5, 0x96, 0x29, 0xe0, 0x84, 0xbb, 0xd8, 0xbe, 0x30, 0x0f, 0xb7, 0xcd, 0xe5, 0xdb, - 0xbc, 0xcd, 0x4b, 0x0d, 0x77, 0x93, 0xb6, 0x6a, 0x68, 0x5e, 0x9c, 0x28, 0x99, 0x05, 0x93, 0x5f, 0x4e, 0x90, 0x93, - 0x7c, 0x15, 0xe5, 0xeb, 0xf3, 0x43, 0xc2, 0x6a, 0xca, 0xad, 0x77, 0x06, 0xd0, 0xf2, 0x9a, 0x45, 0xc4, 0xe0, 0x2d, - 0x0f, 0x79, 0x6e, 0x40, 0xaf, 0xb8, 0x69, 0x4b, 0x2c, 0x49, 0x7e, 0x41, 0xb3, 0x9e, 0x0b, 0x45, 0x6e, 0xe0, 0x4a, - 0x17, 0x9f, 0x5b, 0x7c, 0xa3, 0xa7, 0x20, 0xec, 0xb2, 0x00, 0xcb, 0xab, 0x52, 0x70, 0x6a, 0x01, 0x3f, 0x2e, 0x3a, - 0x38, 0xd8, 0x79, 0x5e, 0xa4, 0x02, 0x09, 0x6b, 0x2d, 0xbf, 0xed, 0x61, 0xb3, 0x22, 0xd7, 0x46, 0x74, 0x31, 0xae, - 0x44, 0x21, 0xd2, 0x78, 0xba, 0xa6, 0xa1, 0xf0, 0xc3, 0x44, 0xa5, 0xbe, 0x58, 0x0c, 0x0b, 0x37, 0xe9, 0x11, 0xca, - 0xb9, 0x08, 0xad, 0x8f, 0xf7, 0xea, 0x73, 0xce, 0x45, 0x68, 0x6e, 0xc0, 0x26, 0xa2, 0x72, 0xe3, 0x63, 0xd2, 0xea, - 0xbe, 0x79, 0x77, 0xe6, 0xa8, 0xe3, 0xd9, 0x39, 0x9c, 0xb4, 0xba, 0x1d, 0xe9, 0x33, 0x51, 0xf7, 0xe7, 0x88, 0xba, - 0x3f, 0xe7, 0xe8, 0xbb, 0x94, 0x10, 0x49, 0xcb, 0x0f, 0xd5, 0xb2, 0xa5, 0xcd, 0xa0, 0xbc, 0xbd, 0xd3, 0x79, 0x2c, - 0x18, 0xbc, 0x99, 0xfa, 0x50, 0x5e, 0x9e, 0x83, 0x0d, 0x2b, 0xb2, 0xa7, 0xb5, 0x76, 0x78, 0x2d, 0x12, 0xe3, 0x1b, - 0x1e, 0xb1, 0x98, 0x9a, 0x7c, 0x69, 0x3d, 0x54, 0x91, 0xdf, 0xbf, 0xd9, 0x3a, 0x9b, 0x5f, 0x4f, 0x99, 0x70, 0xcd, - 0x2d, 0x84, 0xf7, 0xba, 0x43, 0x47, 0x4e, 0xd5, 0xbd, 0xca, 0xb5, 0xf3, 0xda, 0x7c, 0x85, 0xa7, 0xba, 0xa5, 0x7a, - 0xf5, 0x5a, 0x42, 0xc0, 0xbd, 0xb6, 0xc9, 0x51, 0xb7, 0x70, 0x17, 0xdb, 0x75, 0x79, 0xe7, 0x70, 0x72, 0xd4, 0xbd, - 0x0a, 0x66, 0x7a, 0xbc, 0x97, 0x7c, 0xbc, 0x7d, 0xac, 0x98, 0x8f, 0x7b, 0xf2, 0x02, 0x87, 0xba, 0x5a, 0x6c, 0x94, - 0x5f, 0x1e, 0xbb, 0xdd, 0x8e, 0x56, 0x06, 0x1c, 0x19, 0x0e, 0x77, 0x4f, 0x1a, 0xe6, 0x4e, 0x48, 0xcc, 0xc7, 0x70, - 0x20, 0x55, 0x17, 0x6b, 0x92, 0x8a, 0xc7, 0x7d, 0xd2, 0xec, 0x76, 0x42, 0x47, 0xf2, 0x16, 0xc9, 0x3c, 0xb2, 0xe0, - 0x10, 0x3a, 0x4f, 0xf8, 0x94, 0xfa, 0x8c, 0x1f, 0xde, 0xd0, 0xeb, 0x7a, 0x38, 0x63, 0xa5, 0x7b, 0x1b, 0x94, 0x8e, - 0x62, 0x4a, 0x6e, 0x3c, 0xe2, 0xfa, 0xc6, 0x54, 0xab, 0x74, 0xb7, 0x1d, 0x83, 0xcd, 0x63, 0x5c, 0x73, 0xd2, 0x27, - 0x67, 0x81, 0xc5, 0xbb, 0x9d, 0xc3, 0x70, 0x0d, 0x23, 0x92, 0xdf, 0xe7, 0xda, 0xd1, 0x0e, 0x86, 0x0d, 0xd0, 0x9b, - 0xeb, 0x28, 0x71, 0x60, 0x1c, 0xf2, 0x5a, 0x50, 0xe7, 0x6e, 0xf7, 0x5f, 0xff, 0xc7, 0xff, 0xd2, 0x3e, 0xf6, 0xce, - 0xe1, 0xa4, 0x69, 0xc6, 0x5a, 0xdb, 0x95, 0xbc, 0x03, 0x97, 0x34, 0xcb, 0xa0, 0x30, 0xbd, 0xad, 0x8f, 0x53, 0x16, - 0xd5, 0x27, 0x61, 0x3c, 0x72, 0xbb, 0xbb, 0xb1, 0x69, 0x5f, 0xb6, 0xd2, 0x50, 0x57, 0x8b, 0x80, 0x5e, 0x7f, 0xd3, - 0x75, 0x21, 0x73, 0xeb, 0x44, 0x1e, 0x6d, 0xfb, 0xf2, 0x50, 0x79, 0xfa, 0x2a, 0x17, 0x88, 0x52, 0xfd, 0x65, 0x2f, - 0xcd, 0x01, 0xd3, 0xca, 0xbd, 0xa1, 0xdc, 0x75, 0x8a, 0xa0, 0xd6, 0xff, 0xfd, 0x9f, 0xff, 0xe5, 0xbf, 0x99, 0x47, - 0x88, 0x55, 0xfd, 0xeb, 0x7f, 0xff, 0xcf, 0xff, 0xe7, 0x7f, 0xff, 0x57, 0xb8, 0x6b, 0xa2, 0xe3, 0x59, 0x92, 0xa9, - 0x38, 0x65, 0x30, 0x4b, 0x71, 0x17, 0x07, 0xd2, 0x31, 0xa7, 0x2c, 0x13, 0x6c, 0x58, 0xbd, 0x49, 0x74, 0x21, 0x27, - 0x94, 0x27, 0x53, 0x43, 0x27, 0x4f, 0x78, 0x5e, 0x12, 0x54, 0x05, 0xe5, 0x92, 0x70, 0xf3, 0xce, 0x21, 0xe0, 0xfb, - 0x61, 0x97, 0x2f, 0xfd, 0x62, 0x3b, 0x96, 0x86, 0x4c, 0xa0, 0x24, 0x2f, 0xcb, 0x1d, 0x88, 0xad, 0x2c, 0xe1, 0x31, - 0x68, 0x59, 0xc5, 0x72, 0xf7, 0x2a, 0x7d, 0xda, 0x1f, 0xe6, 0x99, 0x60, 0x23, 0x40, 0xb9, 0xf2, 0x13, 0xcb, 0x30, - 0x76, 0x1d, 0x74, 0xc5, 0xf8, 0x2e, 0x97, 0xa3, 0x28, 0x02, 0x3d, 0x3e, 0xfd, 0x53, 0xfe, 0x97, 0x29, 0x68, 0x64, - 0x8e, 0x37, 0x0d, 0x6f, 0xb5, 0x79, 0xfe, 0xa8, 0xd1, 0x98, 0xdd, 0xa2, 0x65, 0x39, 0x03, 0xde, 0x35, 0x99, 0xa4, - 0x63, 0x7b, 0x40, 0x19, 0xff, 0x2e, 0xdc, 0xd8, 0x0d, 0x07, 0x7c, 0xe1, 0x4e, 0x23, 0xcf, 0xff, 0xbc, 0x94, 0x9e, - 0x54, 0xf6, 0x0b, 0xc4, 0xa9, 0xb5, 0xd3, 0xf9, 0x9a, 0xdb, 0x8b, 0x5b, 0x5a, 0xbd, 0x5a, 0xaa, 0xd7, 0xa4, 0xb9, - 0x79, 0xa7, 0xd0, 0x8e, 0xb3, 0xdb, 0x11, 0xf2, 0x63, 0x88, 0x79, 0x4f, 0x9a, 0x78, 0xd2, 0x5a, 0x16, 0xc3, 0x0b, - 0xc1, 0xa7, 0x76, 0x60, 0x9d, 0x86, 0x74, 0x48, 0x47, 0xc6, 0x59, 0xaf, 0xeb, 0x55, 0xd0, 0x3c, 0x9f, 0x1c, 0x6d, - 0x99, 0x4b, 0x83, 0x24, 0x03, 0xea, 0x4e, 0x23, 0xff, 0x1c, 0x4e, 0xe0, 0x72, 0x14, 0xf3, 0x50, 0x04, 0x92, 0x60, - 0xdb, 0x76, 0x78, 0x3e, 0x04, 0x9e, 0xc4, 0x97, 0x16, 0x3c, 0x6d, 0xd5, 0x14, 0xdc, 0xe6, 0xd5, 0x9b, 0x9f, 0xb9, - 0x2f, 0xbb, 0xdb, 0x43, 0x29, 0xaf, 0xdb, 0x77, 0x3a, 0xea, 0xfd, 0xba, 0xe2, 0xae, 0xd2, 0x02, 0xa9, 0x85, 0xb6, - 0xd7, 0x2b, 0xb9, 0xae, 0x6a, 0x7f, 0x14, 0x9e, 0x2b, 0xc1, 0x74, 0xd7, 0x5b, 0xc9, 0x42, 0x68, 0xf5, 0x9a, 0x7c, - 0x57, 0x98, 0x4c, 0xe1, 0x6c, 0x26, 0x1b, 0xa2, 0x76, 0xe7, 0x50, 0x69, 0xba, 0xc0, 0x3d, 0x64, 0x4a, 0x87, 0xca, - 0xa0, 0xd0, 0x8d, 0xf4, 0x51, 0x50, 0xbf, 0x74, 0x6e, 0x05, 0x7c, 0xf2, 0xad, 0xfb, 0xff, 0x00, 0x1a, 0x47, 0x0e, - 0xb2, 0x83, 0x89, 0x00, 0x00}; + 0xe4, 0x2b, 0x20, 0x44, 0x5b, 0x41, 0x6f, 0x36, 0x21, 0x92, 0x92, 0x6c, 0x19, 0x54, 0x93, 0x5b, 0x96, 0x9d, 0xed, + 0x64, 0xfb, 0x16, 0xcb, 0x4e, 0x76, 0xc2, 0x68, 0x4b, 0x10, 0xd1, 0x24, 0x3a, 0x06, 0xd1, 0x0c, 0xd0, 0xa4, 0xa4, + 0x90, 0x38, 0x35, 0x1f, 0x30, 0x55, 0x53, 0x35, 0x4f, 0xf3, 0x32, 0x35, 0xe7, 0x61, 0x3e, 0x62, 0x9e, 0xcf, 0xa7, + 0x9c, 0x1f, 0x98, 0xf9, 0x84, 0xa9, 0xd5, 0x17, 0xa0, 0xc1, 0x8b, 0xac, 0x5c, 0xce, 0x39, 0x53, 0x2e, 0xdb, 0x44, + 0xa3, 0x2f, 0xab, 0x57, 0xaf, 0x5e, 0xf7, 0x6e, 0x9c, 0xec, 0x44, 0x7c, 0x28, 0xee, 0xa6, 0xd4, 0x89, 0xc5, 0x24, + 0xe9, 0x9d, 0xe8, 0x7f, 0x69, 0x18, 0xf5, 0x4e, 0x12, 0x96, 0x7e, 0x74, 0x32, 0x9a, 0x10, 0x36, 0xe4, 0xa9, 0x13, + 0x67, 0x74, 0x44, 0xa2, 0x50, 0x84, 0x01, 0x9b, 0x84, 0x63, 0xea, 0xec, 0xf7, 0x4e, 0x26, 0x54, 0x84, 0xce, 0x30, + 0x0e, 0xb3, 0x9c, 0x0a, 0xf2, 0xe1, 0xfd, 0x97, 0xcd, 0xe3, 0xde, 0x49, 0x3e, 0xcc, 0xd8, 0x54, 0x38, 0xd0, 0x25, + 0x99, 0xf0, 0x68, 0x96, 0xd0, 0xde, 0xfe, 0xfe, 0xcd, 0xcd, 0x8d, 0xff, 0x53, 0xfe, 0xd9, 0x90, 0xa7, 0xb9, 0x70, + 0x5e, 0x92, 0x1b, 0x96, 0x46, 0xfc, 0x06, 0x33, 0x41, 0x5e, 0xfa, 0xe7, 0x71, 0x18, 0xf1, 0x9b, 0x77, 0x9c, 0x8b, + 0xbd, 0x3d, 0x4f, 0x3d, 0xde, 0x9d, 0x9d, 0x9f, 0x13, 0x42, 0xe6, 0x9c, 0x45, 0x4e, 0x6b, 0xb9, 0xac, 0x0a, 0xfd, + 0x34, 0x14, 0x6c, 0x4e, 0x55, 0x13, 0xb4, 0xb7, 0xe7, 0x86, 0x11, 0x9f, 0x0a, 0x1a, 0x9d, 0x8b, 0xbb, 0x84, 0x9e, + 0xc7, 0x94, 0x8a, 0xdc, 0x65, 0xa9, 0xf3, 0x8c, 0x0f, 0x67, 0x13, 0x9a, 0x0a, 0x7f, 0x9a, 0x71, 0xc1, 0x01, 0x92, + 0xbd, 0x3d, 0x37, 0xa3, 0xd3, 0x24, 0x1c, 0x52, 0x78, 0x7f, 0x76, 0x7e, 0x5e, 0xb5, 0xa8, 0x2a, 0xe1, 0x5c, 0x90, + 0xf3, 0xbb, 0xc9, 0x35, 0x4f, 0x3c, 0x84, 0x43, 0x41, 0x52, 0x7a, 0xe3, 0x7c, 0x47, 0xc3, 0x8f, 0xaf, 0xc2, 0x69, + 0x77, 0x98, 0x84, 0x79, 0xee, 0xdc, 0x88, 0x85, 0x9c, 0x42, 0x36, 0x1b, 0x0a, 0x9e, 0x79, 0x02, 0x53, 0xcc, 0xd0, + 0x82, 0x8d, 0x3c, 0x11, 0xb3, 0xdc, 0xbf, 0xdc, 0x1d, 0xe6, 0xf9, 0x3b, 0x9a, 0xcf, 0x12, 0xb1, 0x4b, 0x76, 0x5a, + 0x98, 0xed, 0x10, 0x92, 0x0b, 0x24, 0xe2, 0x8c, 0xdf, 0x38, 0xcf, 0xb3, 0x8c, 0x67, 0x9e, 0x7b, 0x76, 0x7e, 0xae, + 0x6a, 0x38, 0x2c, 0x77, 0x52, 0x2e, 0x9c, 0xb2, 0xbf, 0xf0, 0x3a, 0xa1, 0xbe, 0xf3, 0x21, 0xa7, 0xce, 0xd5, 0x2c, + 0xcd, 0xc3, 0x11, 0x3d, 0x3b, 0x3f, 0xbf, 0x72, 0x78, 0xe6, 0x5c, 0x0d, 0xf3, 0xfc, 0xca, 0x61, 0x69, 0x2e, 0x68, + 0x18, 0xf9, 0x2e, 0xea, 0xca, 0xc1, 0x86, 0x79, 0xfe, 0x9e, 0xde, 0x0a, 0x22, 0xb0, 0x7c, 0x14, 0x84, 0x16, 0x63, + 0x2a, 0x9c, 0xbc, 0x9c, 0x97, 0x87, 0x16, 0x09, 0x15, 0x8e, 0x20, 0xf2, 0x3d, 0xef, 0x2a, 0xdc, 0x53, 0xf5, 0x28, + 0xba, 0x6c, 0xe4, 0x31, 0xb1, 0xb7, 0x27, 0x4a, 0x3c, 0x23, 0x35, 0x35, 0x87, 0x11, 0xba, 0x63, 0xca, 0xf6, 0xf6, + 0xa8, 0x9f, 0xd0, 0x74, 0x2c, 0x62, 0x42, 0x48, 0xbb, 0xcb, 0xf6, 0xf6, 0x3c, 0x41, 0x42, 0xe1, 0x8f, 0xa9, 0xf0, + 0x28, 0x42, 0xb8, 0x6a, 0xbd, 0xb7, 0xe7, 0x29, 0x24, 0x70, 0xa2, 0x10, 0x57, 0xc3, 0x31, 0xf2, 0x35, 0xf6, 0xcf, + 0xef, 0xd2, 0xa1, 0x67, 0xc3, 0x8f, 0x30, 0xdb, 0xdb, 0x0b, 0x85, 0x9f, 0x43, 0x8f, 0x58, 0x20, 0x54, 0x64, 0x54, + 0xcc, 0xb2, 0xd4, 0x11, 0x85, 0xe0, 0xe7, 0x22, 0x63, 0xe9, 0xd8, 0x43, 0x0b, 0x53, 0x66, 0x35, 0x2c, 0x0a, 0x05, + 0xee, 0x07, 0x41, 0x52, 0xd2, 0x83, 0x11, 0x6f, 0x84, 0x07, 0xab, 0xc8, 0x47, 0x4e, 0x4a, 0x88, 0x9b, 0xcb, 0xb6, + 0x6e, 0x3f, 0x0d, 0xd2, 0x86, 0xeb, 0x62, 0x05, 0x25, 0xce, 0x05, 0xc2, 0x6f, 0x88, 0x97, 0x62, 0xdf, 0xf7, 0x05, + 0x22, 0xbd, 0x85, 0xc1, 0x4a, 0x6a, 0xcd, 0xb3, 0x9f, 0x0e, 0x5a, 0x17, 0x81, 0xf0, 0x33, 0x1a, 0xcd, 0x86, 0xd4, + 0xf3, 0x18, 0xce, 0x71, 0x86, 0x48, 0x8f, 0x35, 0x3c, 0x4e, 0x7a, 0xb0, 0xdc, 0xbc, 0xbe, 0xd6, 0x84, 0xec, 0xb4, + 0x90, 0x86, 0x91, 0x1b, 0x00, 0x01, 0xc3, 0x1a, 0x1e, 0x4e, 0x88, 0x9b, 0xce, 0x26, 0xd7, 0x34, 0x73, 0xcb, 0x6a, + 0xdd, 0x1a, 0x59, 0xcc, 0x72, 0xea, 0x0c, 0xf3, 0xdc, 0x19, 0xcd, 0xd2, 0xa1, 0x60, 0x3c, 0x75, 0xdc, 0x06, 0x6f, + 0xb8, 0x8a, 0x1c, 0x4a, 0x6a, 0x70, 0x51, 0x81, 0xbc, 0x1c, 0x35, 0xd2, 0x41, 0xd6, 0x68, 0x5f, 0x60, 0x80, 0x12, + 0x75, 0x75, 0x7f, 0x1a, 0x01, 0x14, 0xa7, 0x30, 0xc7, 0x02, 0xbf, 0x17, 0x30, 0x4b, 0x39, 0x45, 0x26, 0xfa, 0xa9, + 0xbf, 0xbe, 0x51, 0x88, 0xf0, 0x27, 0xe1, 0xd4, 0xa3, 0xa4, 0x47, 0x25, 0x71, 0x85, 0xe9, 0x10, 0x60, 0xad, 0xad, + 0x5b, 0x9f, 0x06, 0xd4, 0xaf, 0x48, 0x0a, 0x05, 0xc2, 0x1f, 0xf1, 0xec, 0x79, 0x38, 0x8c, 0xa1, 0x5d, 0x49, 0x30, + 0x91, 0xd9, 0x6f, 0xc3, 0x8c, 0x86, 0x82, 0x3e, 0x4f, 0x28, 0x3c, 0x79, 0xae, 0x6c, 0xe9, 0x22, 0x9c, 0x93, 0x97, + 0x7e, 0xc2, 0xc4, 0x6b, 0x9e, 0x0e, 0x69, 0x37, 0xb7, 0xa8, 0x8b, 0xc1, 0xba, 0x9f, 0x0a, 0x91, 0xb1, 0xeb, 0x99, + 0xa0, 0x9e, 0x9b, 0x42, 0x0d, 0x17, 0xe7, 0x08, 0x33, 0x5f, 0xd0, 0x5b, 0x71, 0xc6, 0x53, 0x41, 0x53, 0x41, 0xa8, + 0x41, 0x2a, 0x4e, 0xfd, 0x70, 0x3a, 0xa5, 0x69, 0x74, 0x16, 0xb3, 0x24, 0xf2, 0x18, 0x2a, 0x50, 0x81, 0x63, 0x41, + 0x60, 0x8e, 0xa4, 0x97, 0x06, 0xf0, 0xcf, 0xf6, 0xd9, 0x78, 0x82, 0xf4, 0xe4, 0xa6, 0xa0, 0xc4, 0x75, 0xbb, 0x23, + 0x9e, 0x79, 0x7a, 0x06, 0x0e, 0x1f, 0x39, 0x02, 0xc6, 0x78, 0x37, 0x4b, 0x68, 0x8e, 0x68, 0x83, 0xb0, 0x72, 0x19, + 0x35, 0x82, 0x3f, 0x00, 0xc5, 0x17, 0xc8, 0x4b, 0x51, 0x90, 0x76, 0xe7, 0x61, 0xe6, 0x7c, 0xa7, 0x77, 0xd4, 0x33, + 0xc3, 0xcd, 0x86, 0x82, 0x3c, 0xf3, 0x45, 0x36, 0xcb, 0x05, 0x8d, 0xde, 0xdf, 0x4d, 0x69, 0x8e, 0x5f, 0x0b, 0x32, + 0x14, 0xfd, 0xa1, 0xf0, 0xe9, 0x64, 0x2a, 0xee, 0xce, 0x25, 0x63, 0x0c, 0x5c, 0x17, 0x47, 0x50, 0x33, 0xa3, 0xe1, + 0x10, 0x98, 0x99, 0xc6, 0xd6, 0x5b, 0x9e, 0xdc, 0x8d, 0x58, 0x92, 0x9c, 0xcf, 0xa6, 0x53, 0x9e, 0x09, 0xfc, 0x77, + 0xb2, 0x10, 0xbc, 0x42, 0x0d, 0xac, 0xe5, 0x22, 0xbf, 0x61, 0x62, 0x18, 0x7b, 0x02, 0x2d, 0x86, 0x61, 0x4e, 0x9d, + 0xa7, 0x9c, 0x27, 0x34, 0x84, 0x49, 0xa7, 0xfd, 0xd7, 0x22, 0x48, 0x67, 0x49, 0xd2, 0xbd, 0xce, 0x68, 0xf8, 0xb1, + 0x2b, 0x5f, 0xbf, 0xb9, 0xfe, 0x89, 0x0e, 0x45, 0x20, 0x7f, 0x9f, 0x66, 0x59, 0x78, 0x07, 0x15, 0x09, 0x81, 0x6a, + 0xfd, 0x34, 0xf8, 0xfa, 0xfc, 0xcd, 0x6b, 0x5f, 0x6d, 0x12, 0x36, 0xba, 0xf3, 0xd2, 0x72, 0xe3, 0xa5, 0x05, 0x1e, + 0x65, 0x7c, 0xb2, 0x32, 0xb4, 0xc2, 0x5a, 0xda, 0xdd, 0x02, 0x02, 0x25, 0xe9, 0x8e, 0xea, 0xda, 0x86, 0xe0, 0xb5, + 0xa4, 0x79, 0x78, 0x49, 0xcc, 0xb8, 0xb3, 0x24, 0x09, 0x54, 0xb1, 0x97, 0xa2, 0xfb, 0xa1, 0x15, 0xd9, 0xdd, 0x82, + 0x12, 0x09, 0xe7, 0x14, 0x24, 0x0c, 0xc0, 0x38, 0x0c, 0xc5, 0x30, 0x5e, 0x50, 0xd9, 0x59, 0x61, 0x20, 0xa6, 0x45, + 0x81, 0x6f, 0x4b, 0x7a, 0x17, 0x00, 0x88, 0x64, 0x54, 0x44, 0x2c, 0x97, 0x30, 0x61, 0x84, 0x7f, 0x20, 0x8b, 0xd0, + 0xcc, 0x27, 0xd8, 0x69, 0x61, 0xd8, 0x97, 0x81, 0xe2, 0x2e, 0x78, 0xc8, 0xd3, 0x39, 0xcd, 0x04, 0xcd, 0x82, 0xbf, + 0xe3, 0x8c, 0x8e, 0x12, 0x80, 0x62, 0xa7, 0x8d, 0xe3, 0x30, 0x3f, 0x8b, 0xc3, 0x74, 0x4c, 0xa3, 0xe0, 0x56, 0x14, + 0x58, 0x08, 0xe2, 0x8e, 0x58, 0x1a, 0x26, 0xec, 0x17, 0x1a, 0xb9, 0x5a, 0x1c, 0x3c, 0x77, 0xe8, 0xad, 0xa0, 0x69, + 0x94, 0x3b, 0x2f, 0xde, 0xbf, 0x7a, 0xa9, 0x17, 0xb2, 0x26, 0x21, 0xd0, 0x22, 0x9f, 0x4d, 0x69, 0xe6, 0x21, 0xac, + 0x25, 0xc4, 0x73, 0x26, 0xb9, 0xe3, 0xab, 0x70, 0xaa, 0x4a, 0x58, 0xfe, 0x61, 0x1a, 0x85, 0x82, 0xbe, 0xa5, 0x69, + 0xc4, 0xd2, 0x31, 0xd9, 0x69, 0xab, 0xf2, 0x38, 0xd4, 0x2f, 0xa2, 0xb2, 0xe8, 0x72, 0xf7, 0x79, 0x22, 0x27, 0x5e, + 0x3e, 0xce, 0x3c, 0x54, 0xe4, 0x22, 0x14, 0x6c, 0xe8, 0x84, 0x51, 0xf4, 0x55, 0xca, 0x04, 0x93, 0x00, 0x66, 0xb0, + 0x3e, 0x40, 0xa3, 0x54, 0xc9, 0x0a, 0x03, 0xb8, 0x87, 0xb0, 0xe7, 0x69, 0x09, 0x10, 0x23, 0xbd, 0x60, 0x7b, 0x7b, + 0x15, 0xbf, 0xef, 0xd3, 0x40, 0xbd, 0x24, 0x83, 0x0b, 0xe4, 0x4f, 0x67, 0x39, 0xac, 0xb4, 0x19, 0x02, 0xc4, 0x0b, + 0xbf, 0xce, 0x69, 0x36, 0xa7, 0x51, 0x49, 0x1d, 0xb9, 0x87, 0x16, 0x2b, 0x63, 0xe8, 0x7d, 0x21, 0xc8, 0xe0, 0xa2, + 0x6b, 0x33, 0x6e, 0xaa, 0x09, 0x3d, 0xe3, 0x53, 0x9a, 0x09, 0x46, 0xf3, 0x92, 0x97, 0x78, 0x20, 0x46, 0x4b, 0x7e, + 0x92, 0x13, 0x33, 0xbf, 0xa9, 0xc7, 0x30, 0x45, 0x35, 0x8e, 0x61, 0x24, 0xed, 0xf3, 0xb9, 0x14, 0x19, 0x39, 0x66, + 0x08, 0x0b, 0x05, 0x69, 0x8e, 0x50, 0x81, 0xb0, 0x30, 0xe0, 0x2a, 0x5e, 0xa4, 0x47, 0xbb, 0x03, 0x59, 0x4d, 0x7e, + 0x90, 0xb2, 0x1a, 0x38, 0x5a, 0x28, 0xe8, 0xde, 0x9e, 0x47, 0xfd, 0x92, 0x2a, 0xc8, 0x4e, 0x5b, 0xaf, 0x91, 0x85, + 0xac, 0x2d, 0x60, 0xc3, 0xc0, 0x02, 0x53, 0x84, 0x77, 0xa8, 0x9f, 0xf2, 0xd3, 0xe1, 0x90, 0xe6, 0x39, 0xcf, 0xf6, + 0xf6, 0x76, 0x64, 0xfd, 0x52, 0x9d, 0x80, 0x35, 0x7c, 0x73, 0x93, 0x56, 0x10, 0xa0, 0x4a, 0xc4, 0x6a, 0xc1, 0x20, + 0x40, 0x50, 0x49, 0x8d, 0xc3, 0xed, 0x1b, 0xcd, 0x23, 0x70, 0x2f, 0x2f, 0xdd, 0x86, 0xc0, 0x1a, 0x0d, 0x63, 0x6a, + 0x86, 0xbe, 0x7b, 0x46, 0x95, 0x6e, 0x25, 0x35, 0x8f, 0x35, 0xcc, 0xa8, 0x0d, 0xe4, 0x47, 0x74, 0xc4, 0x52, 0x6b, + 0xda, 0x35, 0x90, 0xb0, 0xc0, 0x39, 0x2a, 0xac, 0x05, 0xdd, 0xd8, 0xb5, 0x54, 0x6a, 0xd4, 0xca, 0x2d, 0xc6, 0x52, + 0x91, 0xb0, 0x96, 0x71, 0x40, 0x2f, 0x0a, 0x2c, 0x51, 0x6f, 0x66, 0x93, 0x49, 0x40, 0x07, 0xe2, 0xa2, 0xab, 0xdf, + 0x93, 0x5c, 0x61, 0x2e, 0xa3, 0x3f, 0xcf, 0x68, 0x2e, 0x14, 0x1d, 0x7b, 0x02, 0x67, 0x98, 0xa1, 0x02, 0xf6, 0xdb, + 0x88, 0x8d, 0x67, 0x19, 0xe8, 0x3b, 0xb0, 0x17, 0x69, 0x3a, 0x9b, 0x50, 0xf3, 0xb4, 0x09, 0xb6, 0x37, 0x53, 0x90, + 0x88, 0x39, 0xd0, 0xf4, 0xfd, 0xe4, 0x04, 0xb0, 0x0a, 0xb4, 0x5c, 0xfe, 0x60, 0x3a, 0xa9, 0x96, 0xb2, 0xd4, 0xd1, + 0x56, 0xd7, 0x44, 0x20, 0x2d, 0x91, 0x77, 0xda, 0x0a, 0x7c, 0x21, 0x2e, 0xc8, 0x4e, 0xab, 0xa4, 0x61, 0x8d, 0x55, + 0x05, 0x8e, 0x42, 0xe2, 0x1b, 0xd5, 0x15, 0x92, 0x02, 0xbe, 0x46, 0x2e, 0x7e, 0xbc, 0x46, 0xa9, 0x31, 0x19, 0x80, + 0xaa, 0xe1, 0xc7, 0x17, 0xdb, 0xc8, 0xc9, 0xf0, 0x03, 0x4f, 0xac, 0xbf, 0xab, 0xd8, 0xc6, 0xbc, 0xce, 0x36, 0x56, + 0xa6, 0xe1, 0x4e, 0xcb, 0x26, 0x6e, 0x49, 0x65, 0x7a, 0xa3, 0x57, 0xaf, 0x30, 0x93, 0xc0, 0x54, 0x53, 0xb2, 0xba, + 0x78, 0x1d, 0x4e, 0x68, 0xee, 0x51, 0x84, 0xb7, 0x55, 0x50, 0xe4, 0x09, 0x55, 0x2e, 0x2c, 0xc9, 0x99, 0x83, 0xe4, + 0x64, 0x48, 0x29, 0x66, 0xf5, 0x0d, 0x97, 0x63, 0x3a, 0xc8, 0x2f, 0x2a, 0x7d, 0xce, 0x9a, 0xbc, 0x14, 0xc9, 0x9a, + 0xbe, 0x0d, 0xfe, 0x54, 0x99, 0x42, 0x9a, 0xd4, 0x1b, 0x72, 0x84, 0x77, 0x5a, 0xab, 0x2b, 0x69, 0x6a, 0x55, 0x73, + 0x1c, 0x5c, 0xc0, 0x3a, 0x48, 0x89, 0xe1, 0xb3, 0x5c, 0xfe, 0x5f, 0xdb, 0x69, 0x80, 0xb6, 0x73, 0x20, 0x0c, 0x7f, + 0x94, 0x84, 0xc2, 0x6b, 0xef, 0xb7, 0x40, 0x19, 0x9d, 0x53, 0x10, 0x28, 0x08, 0xad, 0x4f, 0x85, 0xfa, 0xb3, 0x34, + 0x8f, 0xd9, 0x48, 0x78, 0xb1, 0x90, 0x2c, 0x85, 0x26, 0x39, 0x75, 0x44, 0x4d, 0x25, 0x96, 0xec, 0x26, 0x06, 0x62, + 0x2b, 0xf5, 0x2f, 0x6a, 0x20, 0x95, 0x6c, 0x0b, 0xb8, 0x43, 0xa5, 0x4e, 0x57, 0x5c, 0xc6, 0xd4, 0x66, 0xa0, 0x32, + 0xb6, 0xfb, 0xaa, 0xc7, 0x40, 0x33, 0x03, 0x66, 0x69, 0xad, 0x2c, 0xb0, 0x39, 0x84, 0x2e, 0x14, 0xbe, 0xe0, 0x2f, + 0xf9, 0x0d, 0xcd, 0xce, 0x42, 0x00, 0x3e, 0x50, 0xcd, 0x0b, 0x25, 0x08, 0x24, 0xbf, 0x17, 0x5d, 0x43, 0x2f, 0x97, + 0x72, 0xe2, 0x6f, 0x33, 0x3e, 0x61, 0x39, 0x05, 0x65, 0x4d, 0xe1, 0x3f, 0x85, 0x7d, 0x26, 0x37, 0x24, 0x08, 0x1b, + 0x5a, 0xd2, 0xd7, 0xe9, 0xcb, 0x3a, 0x7d, 0x5d, 0xee, 0x3e, 0x1f, 0x1b, 0x06, 0x58, 0xdf, 0xc6, 0x08, 0x7b, 0xda, + 0xa4, 0xb0, 0xe4, 0x9c, 0x1f, 0x23, 0x2d, 0xe1, 0x97, 0x4b, 0x61, 0x59, 0x6e, 0x35, 0x75, 0x91, 0xaa, 0x6d, 0x83, + 0x8a, 0x30, 0x8a, 0x40, 0xb1, 0xcb, 0x78, 0x92, 0x58, 0xa2, 0x0a, 0xb3, 0x6e, 0x29, 0x9c, 0x2e, 0x77, 0x9f, 0x9f, + 0xdf, 0x27, 0x9f, 0xe0, 0xbd, 0x2d, 0xa2, 0x0c, 0xa0, 0x69, 0x44, 0x33, 0xb0, 0x24, 0xad, 0xd5, 0xd2, 0x52, 0xf6, + 0x8c, 0xa7, 0x29, 0x1d, 0x0a, 0x1a, 0x81, 0xa1, 0xc2, 0x88, 0xf0, 0x63, 0x9e, 0x8b, 0xb2, 0xb0, 0x82, 0x9e, 0x59, + 0xd0, 0x33, 0x7f, 0x18, 0x26, 0x89, 0xa7, 0x8c, 0x92, 0x09, 0x9f, 0xd3, 0x0d, 0x50, 0x77, 0x6b, 0x20, 0x97, 0xdd, + 0x50, 0xab, 0x1b, 0xea, 0xe7, 0xd3, 0x84, 0x0d, 0x69, 0x29, 0xba, 0xce, 0x7d, 0x96, 0x46, 0xf4, 0x16, 0xf8, 0x08, + 0xea, 0xf5, 0x7a, 0x2d, 0xdc, 0x46, 0x85, 0x42, 0xf8, 0x62, 0x0d, 0xb1, 0xf7, 0x08, 0x4d, 0x20, 0x32, 0xd2, 0x5b, + 0x6c, 0xe2, 0x07, 0x14, 0x59, 0x92, 0x92, 0x19, 0xe3, 0x4a, 0x71, 0x67, 0x84, 0x23, 0x9a, 0x50, 0x41, 0x0d, 0x37, + 0x07, 0x15, 0x5a, 0x6d, 0xdd, 0x77, 0x25, 0xfe, 0x4a, 0x72, 0x32, 0xbb, 0xcc, 0xac, 0x79, 0x5e, 0x1a, 0xeb, 0xd5, + 0xf2, 0x54, 0xd8, 0xee, 0x0b, 0xb5, 0x3c, 0xa1, 0x10, 0xe1, 0x30, 0x56, 0x56, 0xba, 0xb7, 0x36, 0xa5, 0xaa, 0x0f, + 0xcd, 0xd9, 0xcb, 0x4d, 0xf4, 0xde, 0x80, 0xb9, 0x09, 0x05, 0xe7, 0x9a, 0x29, 0x50, 0x30, 0xfc, 0xd4, 0xb2, 0x9d, + 0x85, 0x49, 0x72, 0x1d, 0x0e, 0x3f, 0xd6, 0xa9, 0xbf, 0x22, 0x03, 0xb2, 0xca, 0x8d, 0xad, 0x57, 0x16, 0xcb, 0xb2, + 0xe7, 0x6d, 0xb8, 0x74, 0x6d, 0xa3, 0x78, 0x3b, 0xad, 0x8a, 0xec, 0xeb, 0x0b, 0xbd, 0x95, 0xda, 0x25, 0x44, 0x4c, + 0xcf, 0xcc, 0x03, 0x2e, 0xf0, 0x49, 0x8a, 0x33, 0xfc, 0x40, 0xd3, 0x1d, 0x98, 0x1b, 0xc5, 0x0a, 0x20, 0x02, 0x2d, + 0x8a, 0x88, 0xe5, 0xdb, 0x31, 0xf0, 0x87, 0x40, 0xf9, 0xcc, 0x1a, 0xe1, 0xa1, 0x80, 0x96, 0x3c, 0x4e, 0x6b, 0xcd, + 0x25, 0x64, 0x5a, 0x9f, 0x30, 0x8c, 0xe6, 0x6f, 0xa0, 0xbb, 0x48, 0x7a, 0x7f, 0xa3, 0x5e, 0x81, 0x56, 0x06, 0x50, + 0xe4, 0x5d, 0x5b, 0x9d, 0xa8, 0x51, 0x80, 0xe6, 0xa9, 0x4c, 0x8a, 0xdc, 0xac, 0x66, 0x3f, 0x6a, 0x8d, 0x5d, 0x99, + 0xe0, 0x9a, 0xe5, 0x72, 0xe2, 0x79, 0x5e, 0x0e, 0x26, 0x9c, 0x51, 0xed, 0xab, 0x49, 0xe4, 0x6b, 0x93, 0xc8, 0x7d, + 0xcb, 0xce, 0x42, 0x15, 0x2d, 0x5b, 0xcd, 0x83, 0xbf, 0x23, 0xbb, 0x12, 0xa8, 0xab, 0x3e, 0xf0, 0x67, 0x54, 0xb2, + 0xdb, 0x84, 0x08, 0xcc, 0xb5, 0x8d, 0xa3, 0x29, 0x0d, 0x18, 0x46, 0xd5, 0x24, 0x43, 0x6a, 0x6b, 0xd4, 0xec, 0xdd, + 0x0c, 0x73, 0xb4, 0xa2, 0xdb, 0x17, 0x85, 0xc6, 0x11, 0x45, 0x7a, 0x6d, 0x6a, 0x4a, 0xb1, 0x85, 0x15, 0x9c, 0x11, + 0xad, 0x08, 0x2b, 0xbd, 0x67, 0x15, 0x37, 0x65, 0xbf, 0x3b, 0x84, 0x64, 0x15, 0x6a, 0x6a, 0x1a, 0xa5, 0x51, 0xad, + 0x32, 0x84, 0x63, 0xa3, 0x93, 0xf2, 0x6a, 0xde, 0x84, 0xb8, 0xc6, 0x21, 0xe1, 0xf6, 0x17, 0x35, 0xab, 0x30, 0xb0, + 0xaa, 0x15, 0x01, 0xb0, 0x54, 0xbe, 0x09, 0xdd, 0x9b, 0x68, 0xa6, 0xd6, 0x8f, 0x85, 0x70, 0x6e, 0x23, 0xdc, 0xc2, + 0x6c, 0xa6, 0x38, 0x57, 0x76, 0x41, 0xe2, 0x7a, 0x5b, 0x8f, 0x62, 0xae, 0xd6, 0x61, 0x0d, 0x89, 0xab, 0xaa, 0xa7, + 0x24, 0x41, 0xb0, 0x61, 0x73, 0x50, 0xee, 0x6c, 0xf9, 0xe0, 0x01, 0xec, 0x6c, 0xb9, 0x5c, 0x23, 0xba, 0x8d, 0x1a, + 0x28, 0xf2, 0x2b, 0xbb, 0x70, 0xb9, 0xbc, 0x15, 0xc8, 0xd3, 0xba, 0x2f, 0xa6, 0xa8, 0x6f, 0x38, 0xee, 0xe9, 0x4b, + 0xa8, 0x25, 0x55, 0xd1, 0xaa, 0xa4, 0x34, 0x1a, 0xea, 0x34, 0x5b, 0x5f, 0x27, 0x61, 0xb1, 0xed, 0xb3, 0x35, 0xee, + 0x25, 0x0b, 0xb5, 0x98, 0xae, 0xa6, 0x7c, 0xa6, 0xbb, 0x66, 0x08, 0xa1, 0x20, 0x97, 0x76, 0xcc, 0xce, 0x26, 0xd3, + 0x72, 0x6f, 0x2f, 0xb7, 0x3a, 0xba, 0x2c, 0xd9, 0xc4, 0x4f, 0x1e, 0x88, 0xe4, 0xfc, 0x2e, 0x95, 0xba, 0xcb, 0x4f, + 0x46, 0x08, 0xad, 0x19, 0xa6, 0xad, 0x2e, 0x18, 0xe4, 0xe1, 0x4d, 0xc8, 0x84, 0x53, 0xf6, 0xa2, 0x0c, 0x72, 0x8f, + 0xa2, 0x85, 0x56, 0x35, 0xfc, 0x8c, 0x82, 0xf2, 0x08, 0x3c, 0xc1, 0xa8, 0xd0, 0x8a, 0xee, 0x87, 0x31, 0x05, 0x5f, + 0xb0, 0xd1, 0x22, 0x4a, 0xcb, 0x70, 0x47, 0x4b, 0x11, 0xdd, 0xf1, 0x66, 0xd8, 0x8b, 0xd5, 0xe6, 0x35, 0x4b, 0x60, + 0x4a, 0xb3, 0x11, 0xcf, 0x26, 0xe6, 0x5d, 0xb1, 0xf2, 0xac, 0x39, 0x23, 0x1b, 0x79, 0x1b, 0xfb, 0xd6, 0xfa, 0x7f, + 0x77, 0xc5, 0xec, 0xae, 0x0c, 0xf6, 0x9a, 0x28, 0x2d, 0xa5, 0xaf, 0x72, 0x09, 0x1a, 0xca, 0xcc, 0x6d, 0x03, 0x5f, + 0xfb, 0x53, 0xbb, 0xca, 0x67, 0xb2, 0xd3, 0xee, 0x96, 0x56, 0x9f, 0xa1, 0x86, 0xae, 0xf2, 0x6d, 0x68, 0x91, 0xca, + 0x67, 0x49, 0xa4, 0x81, 0x65, 0x08, 0x53, 0x4d, 0x47, 0x37, 0x2c, 0x49, 0xaa, 0xd2, 0x5f, 0xc3, 0xd7, 0x73, 0xcd, + 0xd7, 0x33, 0xc3, 0xd7, 0x81, 0x53, 0x00, 0x5f, 0x57, 0xdd, 0x55, 0xcd, 0xb3, 0xb5, 0xdd, 0x99, 0x29, 0x8e, 0x9e, + 0x4b, 0x4b, 0x1a, 0xc6, 0x9b, 0x19, 0x08, 0x50, 0xa9, 0x79, 0x7d, 0xf4, 0xb4, 0x1f, 0x06, 0x4c, 0x40, 0xe5, 0xc5, + 0xa4, 0xb6, 0x93, 0xe2, 0xa3, 0x87, 0x70, 0x5e, 0xd0, 0x92, 0xb2, 0x4f, 0x9f, 0x83, 0x9f, 0xce, 0x9a, 0x0e, 0x08, + 0x31, 0x59, 0xfc, 0xab, 0x94, 0x28, 0x33, 0x3b, 0xa6, 0x67, 0x97, 0x9b, 0xd9, 0x01, 0xa7, 0xaf, 0x66, 0x17, 0xdd, + 0xcf, 0xeb, 0xe5, 0xf4, 0x58, 0x39, 0xbd, 0x6a, 0xbd, 0x97, 0x4b, 0x6f, 0xa5, 0x04, 0x5c, 0xf8, 0xda, 0x44, 0xc9, + 0xca, 0xde, 0x81, 0x07, 0xd8, 0x98, 0x81, 0x82, 0x42, 0x4d, 0xba, 0x14, 0x71, 0x2f, 0x3f, 0xe5, 0xe2, 0x91, 0x9e, + 0x7a, 0xd5, 0xfe, 0x8c, 0x4f, 0xa6, 0xa0, 0x8d, 0xad, 0x90, 0xf4, 0x98, 0xea, 0x01, 0xab, 0xf7, 0xc5, 0x86, 0xb2, + 0x5a, 0x1b, 0xb9, 0x1f, 0x6b, 0xd4, 0x54, 0x5a, 0xcc, 0x3b, 0xad, 0x62, 0x56, 0x16, 0x95, 0x8c, 0x63, 0x93, 0x5b, + 0xe5, 0x6c, 0xd5, 0x29, 0x63, 0x5e, 0xbc, 0xf1, 0x98, 0xe2, 0xc3, 0x0c, 0x78, 0x9d, 0xc5, 0x7e, 0x0c, 0xb9, 0xdb, + 0xeb, 0x5f, 0x54, 0xc8, 0x59, 0x14, 0x2b, 0xe8, 0x5b, 0x14, 0xc5, 0x73, 0x6d, 0x65, 0xe3, 0xe7, 0xdb, 0xcd, 0xe1, + 0xea, 0x9d, 0xb6, 0x16, 0x07, 0x17, 0xf8, 0xf9, 0xba, 0xee, 0x48, 0x16, 0x13, 0x1e, 0xd1, 0xc0, 0xe5, 0x53, 0x9a, + 0xba, 0x05, 0x78, 0x56, 0xf5, 0xe2, 0x47, 0xc2, 0x5b, 0xbc, 0xab, 0xbb, 0x58, 0x83, 0xe7, 0x05, 0x38, 0xc0, 0xbe, + 0x5b, 0x77, 0xbe, 0x7e, 0x4b, 0xb3, 0x5c, 0x6a, 0xa2, 0xa5, 0x52, 0xfb, 0x5d, 0x25, 0x97, 0xbe, 0x0b, 0xb6, 0xd6, + 0xaf, 0x6c, 0x10, 0xb7, 0xed, 0x3f, 0xf2, 0x0f, 0x5c, 0x24, 0x5d, 0xc3, 0x5f, 0xeb, 0x1d, 0xff, 0x93, 0x71, 0x0d, + 0x9f, 0x93, 0x9f, 0xea, 0x9e, 0xe1, 0x99, 0x20, 0xe7, 0xfd, 0x73, 0x63, 0x32, 0xf3, 0x84, 0x0d, 0xef, 0x3c, 0x37, + 0x61, 0xa2, 0x09, 0xe1, 0x37, 0x17, 0x2f, 0xd4, 0x0b, 0xf0, 0x2a, 0x4a, 0x97, 0x76, 0x61, 0x8c, 0x3d, 0x4c, 0x05, + 0x71, 0x77, 0x13, 0x26, 0x76, 0x5d, 0x3c, 0x21, 0x57, 0xf0, 0x63, 0x77, 0xe1, 0xbd, 0x0a, 0x45, 0xec, 0x67, 0x61, + 0x1a, 0xf1, 0x89, 0x87, 0x1a, 0xae, 0x8b, 0xfc, 0x5c, 0x1a, 0x1c, 0x4f, 0x50, 0xb1, 0x7b, 0x85, 0x4f, 0x05, 0x71, + 0xfb, 0x6e, 0x63, 0x82, 0xdf, 0x09, 0x72, 0x75, 0xb2, 0xbb, 0x38, 0x15, 0x45, 0xef, 0x0a, 0xdf, 0x96, 0x5e, 0x7b, + 0xfc, 0x81, 0x78, 0x88, 0xf4, 0x6e, 0x35, 0x34, 0x67, 0x7c, 0xa2, 0xbc, 0xf7, 0x2e, 0xc2, 0xef, 0x65, 0x6c, 0xa5, + 0x62, 0x37, 0x3a, 0xbc, 0xb2, 0x43, 0x5c, 0x2e, 0x7d, 0x04, 0xee, 0xde, 0x9e, 0x55, 0x56, 0xea, 0x0a, 0xf8, 0xb9, + 0x20, 0x35, 0x8b, 0x1c, 0xbf, 0x90, 0x51, 0x9a, 0xe7, 0xc2, 0x4b, 0x91, 0xe9, 0xc6, 0x33, 0xbe, 0x68, 0xbd, 0x37, + 0xd3, 0x81, 0x72, 0x31, 0xf8, 0x4c, 0xd0, 0x2c, 0x14, 0x3c, 0xbb, 0x40, 0xb6, 0xfe, 0x81, 0xff, 0x46, 0xae, 0x06, + 0xce, 0x7f, 0xfa, 0xec, 0xc7, 0xd1, 0x8f, 0xd9, 0xc5, 0x15, 0x7e, 0x4b, 0xf6, 0x4f, 0xbc, 0x7e, 0xe0, 0xed, 0x34, + 0x9b, 0xcb, 0x1f, 0xf7, 0x07, 0xff, 0x08, 0x9b, 0xbf, 0x9c, 0x36, 0x7f, 0xb8, 0x40, 0x4b, 0xef, 0xc7, 0xfd, 0xfe, + 0x40, 0x3f, 0x0d, 0xfe, 0xd1, 0xfb, 0x31, 0xbf, 0xf8, 0xb3, 0x2a, 0xdc, 0x45, 0x68, 0x7f, 0x8c, 0xa7, 0x82, 0xec, + 0x37, 0x9b, 0xbd, 0xfd, 0x31, 0x1e, 0x0b, 0xb2, 0x0f, 0xff, 0x5f, 0x93, 0x77, 0x74, 0xfc, 0xfc, 0x76, 0xea, 0x5d, + 0xf5, 0x96, 0xbb, 0x8b, 0xbf, 0x15, 0xd0, 0xeb, 0xe0, 0x1f, 0x3f, 0xfe, 0x98, 0xbb, 0x5f, 0xf4, 0xc8, 0xfe, 0x45, + 0x03, 0x79, 0x50, 0xfa, 0x67, 0x22, 0xff, 0xf5, 0xfa, 0xc1, 0xe0, 0x1f, 0x1a, 0x0a, 0xf7, 0x8b, 0x1f, 0xaf, 0x4e, + 0x7a, 0xe4, 0x62, 0xe9, 0xb9, 0xcb, 0x2f, 0xd0, 0x12, 0xa1, 0xe5, 0x2e, 0xba, 0xc2, 0xee, 0xd8, 0x45, 0x78, 0x2e, + 0xc8, 0xfe, 0x17, 0xfb, 0x63, 0x3c, 0x12, 0x64, 0xdf, 0xdd, 0x1f, 0xe3, 0x73, 0x41, 0xf6, 0xff, 0xe1, 0xf5, 0x03, + 0xe5, 0x64, 0x5b, 0x4a, 0xff, 0xc6, 0x12, 0x02, 0x1c, 0x61, 0x46, 0xc3, 0xa5, 0x60, 0x22, 0xa1, 0x68, 0x77, 0x9f, + 0xe1, 0x33, 0x89, 0x26, 0x4f, 0x80, 0x17, 0x06, 0x8c, 0x3b, 0x6f, 0x71, 0x09, 0x8b, 0x0d, 0x34, 0xb3, 0x1b, 0x40, + 0x64, 0x07, 0x1c, 0x01, 0x79, 0x20, 0xf0, 0x3c, 0x4c, 0x66, 0x34, 0x0f, 0x68, 0x81, 0xf0, 0x90, 0x9c, 0x09, 0xaf, + 0x8d, 0xf0, 0x53, 0x01, 0x3f, 0x3a, 0x08, 0x9f, 0xe9, 0x20, 0x26, 0xec, 0x64, 0x45, 0x54, 0x29, 0x57, 0x2a, 0x8b, + 0x8b, 0xf0, 0x74, 0xc3, 0x4b, 0x11, 0x83, 0x7b, 0x01, 0xe1, 0xdd, 0x5a, 0xc8, 0x13, 0xdf, 0x10, 0x43, 0x12, 0xef, + 0x33, 0x4a, 0xbf, 0x0b, 0x93, 0x8f, 0x34, 0xf3, 0x6e, 0x71, 0xbb, 0xf3, 0x04, 0x4b, 0x2f, 0xf4, 0x4e, 0x1b, 0x75, + 0xcb, 0x78, 0xd5, 0x47, 0xa1, 0xe2, 0x04, 0x20, 0x65, 0xeb, 0xce, 0x18, 0x58, 0xf1, 0x9d, 0x74, 0xcd, 0x63, 0x95, + 0x85, 0x37, 0x2e, 0xaa, 0xc7, 0x46, 0x59, 0x3a, 0x0f, 0x13, 0x16, 0x39, 0x82, 0x4e, 0xa6, 0x49, 0x28, 0xa8, 0xa3, + 0xe7, 0xeb, 0x84, 0xd0, 0x91, 0x5b, 0xea, 0x0c, 0x33, 0xcb, 0xe2, 0x9c, 0x99, 0xa0, 0x13, 0xec, 0x15, 0x0f, 0x22, + 0x54, 0x5a, 0xef, 0x78, 0x55, 0x05, 0xc0, 0x56, 0x63, 0x7c, 0xcd, 0x36, 0x78, 0xc2, 0x2e, 0xa4, 0x7c, 0xce, 0x71, + 0x46, 0x40, 0x8a, 0x76, 0xfa, 0xee, 0x49, 0x3e, 0x1f, 0xf7, 0x5c, 0x88, 0xcf, 0x70, 0xf2, 0x56, 0x3a, 0x86, 0xa0, + 0x42, 0x4c, 0x5a, 0xdd, 0xf8, 0x84, 0x76, 0xe3, 0x46, 0xc3, 0x28, 0xd1, 0x09, 0x49, 0x07, 0xb1, 0x6a, 0x1e, 0xe2, + 0x08, 0xcf, 0x48, 0xb3, 0x8d, 0xc7, 0xa4, 0x25, 0x9b, 0x74, 0xc7, 0x27, 0x89, 0x1e, 0x66, 0x6f, 0xcf, 0xe3, 0x7e, + 0x12, 0xe6, 0xe2, 0x2b, 0xb0, 0xf6, 0xc9, 0x18, 0x47, 0x84, 0xfb, 0xf4, 0x96, 0x0e, 0xbd, 0x04, 0xe1, 0x48, 0x73, + 0x1a, 0xd4, 0x45, 0x63, 0x62, 0x55, 0x03, 0x2b, 0x82, 0xbc, 0xed, 0x47, 0x83, 0xf6, 0x05, 0x21, 0xc4, 0xdd, 0x69, + 0x36, 0xdd, 0x3e, 0x27, 0x53, 0x11, 0x40, 0x89, 0xa5, 0x2b, 0x93, 0x31, 0x14, 0x75, 0xac, 0x22, 0xef, 0x5c, 0xf8, + 0x82, 0xe6, 0xc2, 0x83, 0x62, 0xb0, 0xff, 0x73, 0x43, 0xd8, 0xee, 0xc9, 0xbe, 0xdb, 0x80, 0x52, 0x49, 0x9c, 0x08, + 0x73, 0x72, 0x8d, 0x82, 0x68, 0x70, 0x70, 0x61, 0x0b, 0x00, 0x59, 0x08, 0x83, 0x5f, 0xf7, 0xa3, 0x41, 0x4b, 0x0e, + 0xde, 0x73, 0xfb, 0x1e, 0x27, 0xb9, 0xd2, 0xd0, 0xfa, 0x79, 0xf0, 0x56, 0x4e, 0x15, 0x05, 0x1a, 0x38, 0xb3, 0x02, + 0xa4, 0xd9, 0x09, 0xbc, 0x99, 0x3d, 0x89, 0x26, 0x0c, 0xa6, 0xb1, 0x80, 0x43, 0x02, 0xf5, 0x31, 0x27, 0x30, 0x62, + 0xd5, 0xec, 0x3a, 0xd0, 0xcf, 0x5f, 0xb8, 0x5f, 0xf4, 0x47, 0x22, 0x98, 0x0b, 0x35, 0xfc, 0x48, 0x2c, 0x97, 0xf0, + 0xff, 0x5c, 0xf4, 0x39, 0xb9, 0x96, 0x45, 0x53, 0x5d, 0x34, 0x86, 0xa2, 0xb7, 0x01, 0x80, 0x8a, 0xf3, 0x52, 0xcb, + 0x52, 0x6b, 0x32, 0x27, 0x12, 0xf6, 0xbd, 0xbd, 0x74, 0x10, 0x37, 0xda, 0x17, 0xe0, 0xe2, 0xcf, 0x44, 0xfe, 0x1d, + 0x13, 0xb1, 0xe7, 0xee, 0xf7, 0x5c, 0xd4, 0x77, 0x1d, 0x58, 0xda, 0x6e, 0xd6, 0x20, 0x0a, 0xc3, 0x49, 0xe3, 0x9d, + 0x08, 0x66, 0x3d, 0xd2, 0xea, 0x7b, 0x4c, 0xb1, 0xf0, 0x10, 0xe1, 0x44, 0x33, 0xce, 0x16, 0x9e, 0xa1, 0x06, 0x15, + 0x0d, 0xf3, 0x3c, 0x43, 0x8d, 0x49, 0x63, 0x8e, 0x82, 0xa4, 0x31, 0x69, 0x78, 0x33, 0x42, 0x48, 0xb3, 0x53, 0x36, + 0x33, 0xe2, 0x2f, 0x46, 0xc1, 0xdc, 0x78, 0x3b, 0x07, 0x72, 0x3b, 0x64, 0x0d, 0x2f, 0x1d, 0xd0, 0x8b, 0xe5, 0xd2, + 0x3d, 0xe9, 0xf7, 0x5c, 0xd4, 0xf0, 0x0c, 0xa1, 0xed, 0x1b, 0x4a, 0x43, 0x08, 0xb3, 0x8b, 0x42, 0x47, 0x93, 0x5e, + 0xd7, 0x22, 0x47, 0x8b, 0x6a, 0xb3, 0x5b, 0x3c, 0x80, 0x16, 0xa5, 0x21, 0xa3, 0x14, 0xd6, 0x29, 0x4c, 0xd3, 0x10, + 0x73, 0x46, 0x5a, 0x98, 0x13, 0xe3, 0xbc, 0x8e, 0x89, 0xa8, 0x08, 0x3e, 0x21, 0x55, 0x75, 0x3c, 0x08, 0x71, 0x74, + 0x41, 0x5e, 0x29, 0x83, 0xa4, 0x6b, 0x5c, 0xe3, 0x34, 0x21, 0xaf, 0x57, 0x22, 0xb8, 0x21, 0x84, 0x57, 0x6e, 0xfc, + 0xe1, 0x2c, 0xcb, 0x68, 0x2a, 0x5e, 0xf3, 0x48, 0xeb, 0x69, 0x34, 0x01, 0x53, 0x09, 0x42, 0xb3, 0x18, 0x94, 0xb4, + 0x8e, 0xd9, 0x19, 0xb3, 0xb5, 0xd7, 0x63, 0x32, 0x53, 0xfa, 0x93, 0x0c, 0xd8, 0x76, 0xc7, 0xda, 0x30, 0xf6, 0x10, + 0x9e, 0xe9, 0x48, 0xae, 0xe7, 0xfb, 0xfe, 0xd8, 0x1f, 0xc2, 0x6b, 0x18, 0x20, 0x47, 0x85, 0xdc, 0x47, 0x5e, 0x4e, + 0x6e, 0xfc, 0x94, 0xde, 0xca, 0x51, 0x3d, 0x54, 0x49, 0x66, 0xb3, 0xbd, 0x4e, 0xe2, 0xae, 0x64, 0x37, 0xb9, 0x9f, + 0xf2, 0x88, 0x02, 0x7a, 0x20, 0x76, 0xaf, 0x8b, 0xe2, 0x30, 0xb7, 0x43, 0x54, 0x15, 0x7c, 0x03, 0xdb, 0x7b, 0x3d, + 0x06, 0x97, 0xaf, 0x54, 0xb6, 0xca, 0xca, 0xca, 0x0f, 0x8e, 0x10, 0x1b, 0x79, 0x63, 0x1f, 0x42, 0x7b, 0x92, 0x84, + 0x28, 0xd8, 0x72, 0x63, 0x9b, 0xa8, 0x26, 0x65, 0x9f, 0x73, 0x12, 0x0d, 0x78, 0xa3, 0x21, 0xdd, 0xd0, 0x33, 0x45, + 0x12, 0x63, 0x84, 0xe7, 0xe5, 0xde, 0x32, 0xf5, 0xbe, 0x24, 0xf5, 0x91, 0xbc, 0x79, 0xdd, 0x9d, 0xdb, 0x80, 0x34, + 0x09, 0xf0, 0x14, 0x0a, 0x6f, 0x82, 0xf0, 0x29, 0xd9, 0xf7, 0x06, 0x7e, 0xff, 0x2f, 0x17, 0xa8, 0xef, 0xf9, 0x7f, + 0x46, 0xfb, 0x8a, 0x71, 0xcc, 0x51, 0x37, 0x51, 0x43, 0x2c, 0x64, 0x08, 0xb3, 0x8d, 0xa5, 0x27, 0x31, 0xc8, 0x70, + 0x1a, 0x4e, 0x68, 0x70, 0x0a, 0x7b, 0xdc, 0xd0, 0xcd, 0x97, 0x18, 0xe8, 0x28, 0x38, 0xd5, 0x9c, 0xc4, 0x77, 0xfb, + 0xcf, 0x44, 0xf9, 0xd4, 0x77, 0xfb, 0x5f, 0x55, 0x4f, 0x7f, 0x71, 0xfb, 0x3f, 0x8b, 0xe0, 0x97, 0x42, 0x3b, 0xbb, + 0x6b, 0x43, 0x3c, 0x32, 0x43, 0x14, 0x6a, 0x61, 0x2c, 0xcc, 0xcd, 0xd0, 0xba, 0x9f, 0x63, 0x8c, 0x0a, 0x36, 0x2a, + 0x59, 0x51, 0xee, 0x8b, 0x70, 0x0c, 0x28, 0xb5, 0x56, 0x20, 0xb7, 0x23, 0xfb, 0xd5, 0x84, 0x81, 0x50, 0x0c, 0xb5, + 0x02, 0x2a, 0xc7, 0xbd, 0x16, 0x5a, 0xd4, 0xea, 0x4a, 0x8d, 0xa9, 0x1e, 0x49, 0x2f, 0xb9, 0xf4, 0x9c, 0xb4, 0xba, + 0xf3, 0x93, 0x71, 0x77, 0xde, 0x68, 0xa0, 0xdc, 0x10, 0xd6, 0x6c, 0x30, 0xbf, 0xc0, 0x1f, 0xc0, 0xa7, 0x67, 0x53, + 0x12, 0xae, 0x4d, 0xaf, 0xa3, 0xa7, 0xd7, 0x68, 0x64, 0x05, 0xea, 0x5a, 0x4d, 0xc7, 0xaa, 0x69, 0x51, 0x28, 0x9c, + 0xac, 0x12, 0xda, 0x31, 0x92, 0x25, 0x90, 0x0e, 0x45, 0x08, 0x39, 0x15, 0x68, 0x63, 0xaf, 0xd0, 0x27, 0x34, 0x97, + 0x3b, 0x16, 0x98, 0xa7, 0x92, 0x11, 0x1e, 0x60, 0x01, 0x9a, 0x96, 0x8e, 0xe0, 0x09, 0x9e, 0x35, 0xda, 0x92, 0xc8, + 0x9b, 0xed, 0x6e, 0xbd, 0xaf, 0xc7, 0x55, 0x5f, 0x78, 0xd6, 0x20, 0x93, 0x12, 0x4b, 0x45, 0xd6, 0x68, 0x14, 0xf5, + 0x68, 0xa7, 0xd9, 0xb7, 0xb5, 0xf8, 0xc3, 0xed, 0x6a, 0x5a, 0x86, 0x91, 0xaf, 0x95, 0x44, 0x65, 0x3e, 0x4b, 0x53, + 0x9a, 0x81, 0x0c, 0x25, 0x02, 0xb3, 0xa2, 0xa8, 0xe4, 0x3a, 0x08, 0x51, 0x4c, 0x49, 0x0a, 0x7c, 0x47, 0x9a, 0x5d, + 0x38, 0xc3, 0x1c, 0xc7, 0x92, 0x6b, 0x10, 0x42, 0xce, 0x4c, 0x42, 0x8b, 0x90, 0x1c, 0x28, 0x21, 0xcc, 0x92, 0x48, + 0x39, 0xa1, 0xfe, 0xe5, 0xee, 0x19, 0xbf, 0xd7, 0x24, 0x1b, 0xb0, 0x8b, 0x40, 0x56, 0x4b, 0x34, 0xdf, 0x0a, 0xc9, + 0x7b, 0x4f, 0xa0, 0x32, 0x38, 0xe2, 0x4b, 0xf6, 0xf7, 0x8c, 0x65, 0x54, 0x6a, 0xe0, 0xbb, 0xc6, 0xec, 0x4b, 0xea, + 0xea, 0x63, 0x62, 0x3b, 0x6f, 0x00, 0x91, 0x21, 0xf8, 0x76, 0x32, 0xb2, 0x56, 0xed, 0x72, 0xf7, 0xf4, 0xcd, 0x26, + 0x13, 0x78, 0xb9, 0xd4, 0xc6, 0xaf, 0xd4, 0x6c, 0x70, 0x58, 0x41, 0x9a, 0xe8, 0x1f, 0x81, 0x97, 0x48, 0x05, 0x29, + 0xf4, 0x52, 0xa0, 0xa2, 0xcb, 0xdd, 0xd3, 0xf7, 0x5e, 0x2a, 0x5d, 0x4b, 0x08, 0xdb, 0xd3, 0xf6, 0x38, 0xf1, 0x62, + 0x42, 0x91, 0x9a, 0x7b, 0xc9, 0xb8, 0xb8, 0x25, 0xbe, 0x83, 0x58, 0xbe, 0x04, 0xfb, 0x61, 0xc0, 0x2e, 0x48, 0xa2, + 0x31, 0x40, 0x12, 0x84, 0x93, 0x9a, 0x59, 0x46, 0x60, 0x01, 0xe4, 0x58, 0xe7, 0xb0, 0x12, 0xbe, 0x52, 0xfc, 0x10, + 0x4e, 0xe4, 0xa8, 0xa2, 0x50, 0xa2, 0xe3, 0xe5, 0x5a, 0x5e, 0x5a, 0x65, 0x8d, 0x7e, 0x0b, 0x96, 0x93, 0x79, 0x78, + 0xad, 0xbb, 0x2e, 0x0b, 0x9e, 0x99, 0x04, 0xb2, 0xcb, 0xdd, 0xd3, 0x57, 0x3a, 0x87, 0x6c, 0x1a, 0x1a, 0x6e, 0xbf, + 0x66, 0x61, 0x9e, 0xbe, 0xf2, 0xab, 0xb7, 0xb2, 0xf2, 0xe5, 0xee, 0xe9, 0x87, 0x4d, 0xd5, 0xa0, 0xbc, 0x98, 0x55, + 0x26, 0xbe, 0x84, 0x6f, 0x41, 0x93, 0x60, 0xa1, 0x45, 0x43, 0xc0, 0x0a, 0x2c, 0xc5, 0x51, 0x90, 0x17, 0xa5, 0x67, + 0xe4, 0x19, 0xce, 0x88, 0x8c, 0x02, 0xd5, 0x57, 0x4d, 0x2b, 0x79, 0x8c, 0xa7, 0xe7, 0x43, 0x3e, 0xa5, 0x5b, 0x42, + 0x43, 0xb7, 0xc8, 0x67, 0x13, 0x48, 0x9e, 0x91, 0xa0, 0x33, 0xbc, 0xd3, 0x42, 0xdd, 0xba, 0xf0, 0xca, 0x24, 0x91, + 0xf2, 0x9a, 0x64, 0xc1, 0x31, 0x69, 0xe1, 0x84, 0xb4, 0x70, 0x48, 0xf2, 0x41, 0x4b, 0x89, 0x87, 0x6e, 0x58, 0xf6, + 0xab, 0x84, 0x0c, 0xe4, 0x85, 0xe9, 0xdd, 0xaa, 0xc4, 0x6f, 0xd4, 0x0d, 0xa5, 0xeb, 0x51, 0x4a, 0xf4, 0x48, 0x92, + 0xc5, 0x0b, 0x8f, 0x63, 0x2e, 0x3b, 0x3e, 0x67, 0xd7, 0x09, 0xa4, 0x96, 0xc0, 0xac, 0xb0, 0x40, 0x41, 0x59, 0xb5, + 0xad, 0xab, 0x86, 0xbe, 0x5c, 0x27, 0x8e, 0x43, 0x1f, 0x18, 0x37, 0x0e, 0x75, 0x26, 0x4e, 0xbe, 0xde, 0xe4, 0xd1, + 0xde, 0x9e, 0xa7, 0x1a, 0xfd, 0x22, 0x3c, 0x6e, 0xde, 0x57, 0x81, 0xbb, 0x6f, 0x15, 0xaf, 0x88, 0x90, 0x84, 0xbf, + 0xd1, 0x48, 0x2e, 0x0a, 0x88, 0x42, 0x7b, 0x61, 0x1d, 0x83, 0x06, 0x78, 0xa9, 0xe9, 0xd5, 0xa7, 0xdf, 0x68, 0x94, + 0x41, 0xda, 0x3a, 0xb6, 0x6e, 0x71, 0x56, 0xcc, 0xbd, 0x32, 0xf9, 0xa7, 0xb5, 0x96, 0x31, 0x65, 0x40, 0x40, 0xcc, + 0xa6, 0x59, 0x66, 0x26, 0x63, 0x6d, 0x09, 0x06, 0xf5, 0xbe, 0xd2, 0x69, 0x0b, 0x58, 0xe6, 0x57, 0xe9, 0x4a, 0x86, + 0x9d, 0x75, 0x50, 0x60, 0x2a, 0x41, 0x50, 0x0a, 0x2a, 0x35, 0x0a, 0x4d, 0xde, 0x2f, 0xd6, 0xb3, 0x2e, 0x71, 0x8e, + 0xb4, 0x8f, 0x4b, 0x42, 0x21, 0x91, 0xd5, 0x29, 0x91, 0xf2, 0x82, 0x4c, 0xb7, 0x93, 0xfc, 0xa9, 0x45, 0xf2, 0x4f, + 0x09, 0xb5, 0xc8, 0x5f, 0x79, 0x38, 0x7c, 0xae, 0x5d, 0x0b, 0xb9, 0x79, 0x75, 0x36, 0x25, 0xe0, 0x43, 0xab, 0x63, + 0xb4, 0x16, 0x55, 0xdc, 0xc2, 0x50, 0xec, 0x1d, 0x22, 0xbd, 0x90, 0xd8, 0x84, 0x80, 0xbd, 0x2a, 0xa6, 0x06, 0x43, + 0x6f, 0x72, 0xe9, 0xd9, 0x1c, 0xf0, 0xf4, 0xc3, 0xfd, 0xe1, 0xd0, 0xb3, 0xe9, 0xfa, 0xce, 0xb5, 0xb2, 0x3f, 0x61, + 0xd6, 0xd6, 0xc6, 0xad, 0xe7, 0x82, 0xc2, 0xf8, 0x65, 0x18, 0xbb, 0xce, 0x7c, 0x56, 0x36, 0xa1, 0x91, 0x7f, 0x00, + 0x6d, 0xbb, 0x2d, 0x6b, 0x50, 0xab, 0x5b, 0xe0, 0x47, 0x2a, 0x07, 0x35, 0xcc, 0xb6, 0xb0, 0x8f, 0x53, 0x59, 0x81, + 0xa6, 0xd1, 0xe6, 0xd7, 0x4f, 0x0b, 0x4d, 0x26, 0x0a, 0x34, 0xb4, 0x00, 0xfe, 0xa7, 0x48, 0x1e, 0xe8, 0x46, 0xca, + 0x05, 0x40, 0xd0, 0x54, 0xe2, 0xa9, 0x42, 0x98, 0xeb, 0x56, 0xce, 0xf7, 0x17, 0x3b, 0x84, 0x4c, 0x2b, 0xe7, 0xe3, + 0xbb, 0x2a, 0xf7, 0x0a, 0xc8, 0x02, 0x05, 0x60, 0x3c, 0x96, 0x05, 0x2a, 0x7a, 0x79, 0x66, 0xaa, 0x4b, 0x03, 0xd2, + 0xaf, 0xf4, 0x6d, 0x2b, 0xb2, 0x29, 0xbd, 0x72, 0xea, 0xbd, 0x41, 0xc3, 0xca, 0xdb, 0x5d, 0x78, 0xfb, 0x42, 0x48, + 0x18, 0xe1, 0xf9, 0xbd, 0xac, 0x6d, 0xfa, 0x2d, 0x3e, 0xae, 0x26, 0xb0, 0xac, 0x2c, 0x8a, 0xcf, 0xd2, 0x9c, 0x66, + 0xe2, 0x29, 0x1d, 0xf1, 0x0c, 0x42, 0x16, 0x25, 0x4e, 0x50, 0xb1, 0x6b, 0xb9, 0xed, 0xe4, 0xfc, 0xac, 0x38, 0xc1, + 0xca, 0x04, 0xe5, 0xaf, 0x8f, 0x32, 0x66, 0x7d, 0xb9, 0xda, 0x6a, 0xba, 0xb7, 0xf7, 0xbe, 0x42, 0x93, 0x86, 0x52, + 0x42, 0x61, 0x31, 0x2d, 0xa5, 0xd2, 0xe8, 0x40, 0xee, 0xae, 0x57, 0xba, 0x00, 0x0c, 0xc3, 0xb0, 0x79, 0xcf, 0x0b, + 0x22, 0x8a, 0xf1, 0x2a, 0x8b, 0xd7, 0xae, 0x09, 0x66, 0x9b, 0x2d, 0xc0, 0xe1, 0xc1, 0xd0, 0x56, 0xbe, 0xa2, 0xbc, + 0x4a, 0x87, 0x2d, 0x61, 0x38, 0x03, 0x64, 0x79, 0xd2, 0x08, 0xb1, 0x28, 0x70, 0xa3, 0x51, 0xf2, 0x11, 0xf4, 0xca, + 0x18, 0xe7, 0x7e, 0x0c, 0x09, 0xb0, 0xb5, 0x2d, 0x8b, 0x10, 0x56, 0x79, 0x39, 0x56, 0x26, 0xc1, 0xe9, 0x8b, 0x4d, + 0x1e, 0x65, 0x43, 0xd4, 0x54, 0x4a, 0x1d, 0xa8, 0x91, 0xa1, 0xb2, 0x81, 0x3f, 0xf7, 0x98, 0x56, 0xdc, 0x4c, 0xd8, + 0x0c, 0x18, 0xf0, 0x4b, 0xe1, 0xa9, 0x58, 0x14, 0xc8, 0x0c, 0xee, 0xcf, 0xbc, 0xda, 0xd0, 0x5d, 0x2e, 0x9b, 0x61, + 0x8d, 0xb8, 0xd8, 0x46, 0x13, 0x97, 0x61, 0xbd, 0xb3, 0x8a, 0x97, 0xee, 0xaa, 0x1c, 0x6a, 0x61, 0xb8, 0x60, 0x95, + 0x47, 0x62, 0x4d, 0x7f, 0x57, 0xa5, 0x45, 0x97, 0x95, 0x40, 0x0d, 0xa3, 0x37, 0xce, 0x6b, 0xb9, 0x06, 0xb4, 0x00, + 0xfa, 0x5a, 0x3c, 0x17, 0xd6, 0x8a, 0x1a, 0x1f, 0xb6, 0x1c, 0xd3, 0x92, 0xfa, 0xef, 0x20, 0xd3, 0x65, 0x75, 0xcf, + 0xbf, 0x90, 0xb2, 0x90, 0xe1, 0xbc, 0xc6, 0xd8, 0x33, 0xc9, 0xd8, 0x11, 0xe8, 0x69, 0x26, 0xf5, 0xbb, 0xaf, 0x13, + 0x5e, 0x98, 0x96, 0x72, 0x9a, 0xc4, 0x3e, 0x94, 0xc1, 0x72, 0xeb, 0xf7, 0xca, 0x6a, 0x04, 0x8c, 0x40, 0x12, 0x10, + 0xd6, 0x9c, 0x3d, 0x43, 0x38, 0x6f, 0x34, 0xba, 0xf9, 0x09, 0xad, 0x5c, 0x24, 0x15, 0x8c, 0x0c, 0xe2, 0xb9, 0x40, + 0xf0, 0x35, 0x19, 0x0a, 0x11, 0x7f, 0x93, 0x9b, 0x9d, 0x83, 0xab, 0xfd, 0xf4, 0x9d, 0x67, 0x73, 0x35, 0xbb, 0x6e, + 0x19, 0x33, 0x85, 0xf9, 0x78, 0x55, 0xbc, 0xe5, 0xed, 0xfd, 0xf9, 0x1d, 0x00, 0xf7, 0x4e, 0x1b, 0x43, 0x2e, 0x1a, + 0xea, 0x0a, 0xc5, 0x12, 0xca, 0xdd, 0xd7, 0x45, 0x55, 0x5a, 0xa2, 0x3d, 0x58, 0x57, 0x54, 0xa6, 0xac, 0x20, 0x79, + 0x51, 0xe4, 0xb4, 0x8a, 0xee, 0xaf, 0xe4, 0x5f, 0x4a, 0xe1, 0xb2, 0xee, 0x6c, 0x3f, 0x9b, 0x12, 0x81, 0x2d, 0x42, + 0x7d, 0xbb, 0x2d, 0xf4, 0x51, 0x81, 0x09, 0xfb, 0x5a, 0x0b, 0xc5, 0x5f, 0x36, 0x09, 0x45, 0x9c, 0xe9, 0x2d, 0x2f, + 0x05, 0x62, 0xfb, 0x01, 0x02, 0x51, 0x3b, 0xd9, 0x8d, 0x4c, 0x04, 0x75, 0xa4, 0x26, 0x13, 0xeb, 0x4b, 0x4a, 0x32, + 0xcc, 0xf4, 0x6a, 0xf4, 0x3a, 0xcb, 0x25, 0x1b, 0xb4, 0xc0, 0x89, 0xe4, 0xba, 0xf0, 0xb3, 0xad, 0x7e, 0x5a, 0x9c, + 0x58, 0x39, 0x81, 0x3d, 0x56, 0x9a, 0x2c, 0xc8, 0x87, 0x14, 0x67, 0x4f, 0xe6, 0x64, 0x49, 0x9a, 0xd6, 0x14, 0xa4, + 0x09, 0x9c, 0xb0, 0x32, 0xca, 0x04, 0x10, 0x4b, 0x59, 0xa1, 0x0d, 0x48, 0x6f, 0x63, 0xf2, 0x9f, 0x31, 0x2f, 0x3f, + 0xad, 0x89, 0xd6, 0xe4, 0x8a, 0x52, 0x1f, 0x6a, 0xe9, 0x06, 0x1a, 0x02, 0xad, 0x1f, 0xee, 0x48, 0x13, 0xb4, 0x12, + 0xe5, 0xc8, 0x96, 0x43, 0xb8, 0x05, 0x2e, 0xb4, 0x9d, 0xf7, 0x2a, 0xc0, 0xbb, 0x41, 0x9a, 0x60, 0x6e, 0xd1, 0xf5, + 0x0b, 0x22, 0x6a, 0xac, 0x24, 0x26, 0xda, 0x52, 0xc2, 0xa1, 0x24, 0x53, 0x41, 0xb2, 0x41, 0xeb, 0x02, 0x14, 0xd0, + 0x6e, 0x72, 0x92, 0x55, 0x26, 0x70, 0xd2, 0x68, 0xa0, 0xd0, 0x8c, 0x1a, 0x0f, 0x58, 0x23, 0xb9, 0xc0, 0x14, 0x27, + 0xca, 0x30, 0x39, 0xdb, 0xdb, 0xf3, 0xc2, 0x6a, 0xdc, 0x41, 0x72, 0x81, 0x30, 0x5f, 0x2e, 0x3d, 0x09, 0x56, 0x88, + 0x96, 0xcb, 0xd0, 0x06, 0x4b, 0xbe, 0x86, 0x66, 0xd3, 0xbe, 0x20, 0x53, 0x29, 0x00, 0xa7, 0x00, 0x61, 0x83, 0x78, + 0xa1, 0x76, 0xee, 0x85, 0xe0, 0x8c, 0x6a, 0x64, 0x83, 0xa4, 0xd1, 0xbe, 0xb0, 0x18, 0xd7, 0x20, 0xb9, 0x20, 0x61, + 0xc1, 0xf7, 0xf6, 0x76, 0x72, 0x2d, 0x22, 0x7f, 0x02, 0x51, 0xf6, 0x93, 0x94, 0x2c, 0xaa, 0x43, 0x7b, 0x35, 0x56, + 0x9d, 0x01, 0x25, 0x45, 0xe9, 0x65, 0x35, 0xf5, 0x6a, 0x49, 0x10, 0x65, 0x25, 0xac, 0x63, 0xc1, 0x7d, 0xb0, 0xec, + 0x4b, 0x32, 0x7f, 0x26, 0xca, 0x24, 0xeb, 0x5f, 0x36, 0xa6, 0x56, 0xfb, 0xbe, 0x1f, 0x66, 0x63, 0x19, 0xc9, 0x30, + 0x51, 0x58, 0x49, 0xfc, 0x07, 0x1a, 0x4c, 0x6b, 0xe0, 0x41, 0x39, 0xd6, 0x05, 0x51, 0xe0, 0x1b, 0xd5, 0xc6, 0x9c, + 0x26, 0xf9, 0x69, 0xa3, 0x97, 0x41, 0x41, 0xf2, 0xd5, 0x6f, 0x85, 0xe4, 0x50, 0x43, 0xa2, 0xc8, 0x63, 0x05, 0x67, + 0x5b, 0x70, 0xf1, 0x93, 0x58, 0xc1, 0xd9, 0x76, 0xdc, 0x1a, 0x4c, 0xfd, 0xbc, 0x0d, 0x3e, 0x8b, 0x37, 0x28, 0x40, + 0xab, 0x02, 0x0b, 0xca, 0xa3, 0x55, 0xdd, 0x4b, 0xb1, 0x52, 0x10, 0xa6, 0x82, 0x78, 0xac, 0xbe, 0x01, 0x2a, 0x6d, + 0xd4, 0x32, 0x7c, 0x59, 0x30, 0x45, 0x96, 0x4b, 0xa0, 0x9e, 0xb9, 0x02, 0xe4, 0xa4, 0x7d, 0xed, 0xd3, 0xbd, 0x3d, + 0xb0, 0x0d, 0x40, 0x89, 0xf3, 0x87, 0xe1, 0x54, 0xcc, 0x32, 0x50, 0xa5, 0x72, 0xf3, 0x1b, 0x8a, 0xe1, 0x1c, 0x88, + 0x2c, 0x83, 0x1f, 0x50, 0x30, 0x0d, 0xf3, 0x9c, 0xcd, 0x55, 0x99, 0xfe, 0x8d, 0x39, 0x31, 0xa4, 0x9c, 0x2b, 0x9d, + 0x30, 0x43, 0xdd, 0x4c, 0xd3, 0x69, 0x1d, 0x6d, 0xcf, 0xe7, 0x34, 0x15, 0x2f, 0x59, 0x2e, 0x68, 0x0a, 0xd3, 0xaf, + 0x28, 0x0e, 0x66, 0x94, 0x23, 0xd8, 0xb0, 0xb5, 0x56, 0x61, 0x14, 0xdd, 0xdb, 0x44, 0xd4, 0x75, 0xa0, 0x38, 0x4c, + 0xa3, 0x44, 0x0d, 0x62, 0xa7, 0x33, 0x9a, 0x14, 0xce, 0xb2, 0xa6, 0x9d, 0x4e, 0x53, 0x29, 0x1b, 0x92, 0xbb, 0x7b, + 0x8c, 0x18, 0x49, 0x60, 0xa4, 0xe7, 0xbd, 0x5a, 0x0b, 0x04, 0xbc, 0xb7, 0x2c, 0x82, 0x3d, 0x13, 0x2c, 0x2c, 0x8e, + 0xea, 0xd7, 0xe1, 0x2c, 0x05, 0xc9, 0xc6, 0x43, 0x6d, 0x9b, 0x84, 0x83, 0xa4, 0x93, 0x47, 0xdb, 0x2d, 0xab, 0x57, + 0x46, 0x72, 0x18, 0x69, 0xc1, 0x1e, 0xca, 0x98, 0xd1, 0xc2, 0x90, 0x17, 0x32, 0x5b, 0xf1, 0x52, 0x90, 0x9f, 0xe0, + 0xd4, 0xd0, 0x0b, 0x31, 0x49, 0x56, 0x0e, 0xc7, 0x74, 0x2f, 0x4b, 0xed, 0xff, 0x52, 0x78, 0xaf, 0xf1, 0x0b, 0x08, + 0xeb, 0x7e, 0x5d, 0x55, 0x5f, 0x0f, 0xe7, 0x7e, 0x5d, 0x21, 0xe8, 0xeb, 0x60, 0xad, 0x9e, 0x15, 0xc6, 0xed, 0xf8, + 0xc7, 0x7e, 0xcb, 0x35, 0xda, 0xd2, 0xb7, 0x2a, 0x88, 0xa4, 0x12, 0x2d, 0xe5, 0x7e, 0xc0, 0x55, 0x9a, 0x1a, 0xa4, + 0xcb, 0xd5, 0x2d, 0x24, 0xaa, 0x13, 0x0c, 0x95, 0x0e, 0xbf, 0x6d, 0x79, 0xb4, 0x8c, 0xc9, 0x94, 0x9d, 0xf1, 0x36, + 0xcc, 0xc4, 0x2e, 0xec, 0x32, 0xbe, 0x76, 0x12, 0x2f, 0x26, 0xe0, 0x41, 0x7b, 0xd8, 0x10, 0x96, 0xb1, 0x9d, 0xab, + 0x93, 0x40, 0x76, 0xff, 0x84, 0x1b, 0xdd, 0xad, 0x6e, 0x65, 0x7c, 0x00, 0xfb, 0x1f, 0xe1, 0xd8, 0x1c, 0x8f, 0xa3, + 0x9a, 0x03, 0xd3, 0x60, 0x51, 0x94, 0x4e, 0x01, 0xae, 0x94, 0xb7, 0x14, 0x61, 0x5e, 0xc8, 0xf0, 0xf6, 0x37, 0xf8, + 0x7b, 0xcd, 0x12, 0x47, 0x25, 0xc7, 0x79, 0xfe, 0x50, 0x8e, 0xa8, 0xc0, 0x2f, 0xa3, 0xf7, 0x40, 0xc7, 0x92, 0x42, + 0x0b, 0x43, 0x45, 0xcf, 0xb8, 0x9e, 0xc8, 0xd6, 0xac, 0x54, 0x4c, 0xcb, 0x8c, 0x1a, 0x39, 0xcc, 0x86, 0x34, 0x4e, + 0x63, 0x65, 0x8b, 0x72, 0x57, 0xd5, 0xc6, 0x45, 0x5b, 0xb0, 0x58, 0x05, 0x16, 0x97, 0x4b, 0xaf, 0x8e, 0x6a, 0xc2, + 0xac, 0x38, 0x06, 0xc2, 0xcc, 0x4a, 0xa8, 0xa8, 0x69, 0xd6, 0xaa, 0x8d, 0x87, 0x56, 0xf3, 0x89, 0x8c, 0x6e, 0x5e, + 0x83, 0xc3, 0x76, 0x21, 0xa8, 0xe6, 0xb6, 0x4f, 0x01, 0xab, 0xd9, 0x95, 0x03, 0x59, 0x18, 0xfa, 0xb6, 0xcc, 0x94, + 0xad, 0x52, 0x5a, 0x37, 0xe0, 0x17, 0xdd, 0x93, 0x2b, 0xab, 0x51, 0xb7, 0xfe, 0xde, 0xca, 0x35, 0x7a, 0xc6, 0xb7, + 0xe5, 0x1a, 0xd5, 0xb4, 0xdd, 0x9d, 0x16, 0xba, 0x3f, 0x2b, 0x55, 0x8d, 0xb5, 0xb9, 0xca, 0x6f, 0x18, 0xae, 0x0d, + 0xb4, 0xa9, 0xd0, 0x6c, 0xb8, 0xca, 0x59, 0x51, 0x8c, 0xca, 0xb3, 0x04, 0x32, 0x75, 0x67, 0xa4, 0xe8, 0x5f, 0x5b, + 0x8d, 0xf2, 0x40, 0xae, 0xf7, 0x0d, 0x19, 0x27, 0xfc, 0x3a, 0x4c, 0xde, 0xc3, 0x78, 0xd5, 0xcb, 0x17, 0x77, 0x51, + 0x16, 0x0a, 0xaa, 0xb9, 0x4b, 0x05, 0xc3, 0x37, 0x16, 0x0c, 0xdf, 0x28, 0x3e, 0x5d, 0xb5, 0xc7, 0x8b, 0x97, 0x65, + 0x07, 0xc1, 0xa8, 0x30, 0x2c, 0x63, 0x22, 0x36, 0x8f, 0xb1, 0xca, 0xc2, 0x26, 0x25, 0x0b, 0x9b, 0x08, 0x6f, 0xb5, + 0x2b, 0xcf, 0xfb, 0x7e, 0x73, 0x2f, 0xeb, 0x9c, 0xed, 0xfb, 0x6a, 0xe3, 0x7f, 0x1f, 0xdc, 0xdb, 0xc6, 0xe2, 0x72, + 0x07, 0xfe, 0x81, 0x4c, 0x56, 0x51, 0x20, 0x3f, 0x85, 0xa4, 0x03, 0x41, 0x7a, 0xd6, 0x91, 0x83, 0x4a, 0x4e, 0x99, + 0x3c, 0x20, 0x6f, 0x38, 0xcb, 0x05, 0x9f, 0xe8, 0x3e, 0x73, 0x7d, 0xce, 0x48, 0xbe, 0x04, 0x57, 0xb4, 0x8c, 0xb5, + 0x07, 0xf5, 0x93, 0x5c, 0x8b, 0x8f, 0x2c, 0x8d, 0x82, 0x1c, 0x6b, 0x29, 0x92, 0x07, 0x59, 0x41, 0x4c, 0xae, 0xf1, + 0xfa, 0x3b, 0x3c, 0x62, 0x29, 0xcb, 0x63, 0x9a, 0x79, 0x1c, 0x2d, 0xb6, 0x0d, 0xc6, 0x21, 0x20, 0xa3, 0x06, 0xc3, + 0x5f, 0x56, 0x47, 0xfe, 0x7c, 0xe8, 0x0d, 0xfc, 0x40, 0x13, 0x2a, 0x62, 0x1e, 0x41, 0x5a, 0x8a, 0x1f, 0x95, 0x47, + 0x9a, 0xf6, 0xf6, 0x76, 0x3c, 0x57, 0xba, 0x25, 0xe0, 0xf0, 0xb7, 0xfd, 0x06, 0xf5, 0x17, 0x70, 0x3a, 0xa7, 0x1a, + 0x9a, 0xa2, 0x05, 0x5d, 0x3d, 0xc8, 0x22, 0xfc, 0x8f, 0xf4, 0x0e, 0xa7, 0xa8, 0x28, 0x02, 0x05, 0xb5, 0x3b, 0x62, + 0x34, 0x89, 0x5c, 0xfc, 0x91, 0xde, 0x05, 0xe5, 0x79, 0x71, 0x79, 0xbc, 0x59, 0x2e, 0xa0, 0xcb, 0x6f, 0x52, 0x17, + 0x57, 0x83, 0x04, 0x8b, 0x02, 0xf3, 0x8c, 0x8d, 0x81, 0x38, 0xff, 0x46, 0xef, 0x02, 0xd5, 0x1f, 0xb3, 0x4e, 0xeb, + 0xa1, 0x85, 0x41, 0xbd, 0x6f, 0x15, 0xdb, 0xcb, 0xa0, 0x0d, 0x8a, 0x81, 0x6c, 0x7b, 0x41, 0x6a, 0xf5, 0x2a, 0xf3, + 0x10, 0xa1, 0xe2, 0xa1, 0x53, 0xc1, 0xdf, 0xd9, 0xa2, 0x4d, 0xd4, 0x32, 0x5f, 0x57, 0x1a, 0x51, 0x68, 0x50, 0x65, + 0x7a, 0x5c, 0x7a, 0xa9, 0xd9, 0x75, 0xfa, 0x08, 0x82, 0xe5, 0x08, 0xfb, 0x4e, 0xe8, 0x4e, 0x83, 0x2f, 0x55, 0x42, + 0x48, 0x15, 0x49, 0x7a, 0x55, 0xb5, 0x73, 0x2e, 0x3d, 0xc0, 0x3b, 0x24, 0xb4, 0x84, 0xf2, 0x40, 0x66, 0x61, 0xb2, + 0x45, 0x7f, 0x10, 0xc4, 0x5b, 0x98, 0x29, 0x04, 0xa9, 0x8d, 0x45, 0x51, 0x00, 0x15, 0x6a, 0xfa, 0x52, 0x09, 0x80, + 0x70, 0x86, 0x7d, 0x4d, 0x6a, 0x66, 0x52, 0x6a, 0xfa, 0x16, 0xc6, 0xb7, 0x48, 0x49, 0x2a, 0x91, 0x21, 0x95, 0x48, + 0x29, 0xf4, 0xf4, 0xe2, 0x6a, 0x12, 0xb2, 0x17, 0xb4, 0x3c, 0x3f, 0xa7, 0xd6, 0x3c, 0xab, 0x81, 0xe5, 0xc9, 0x7e, + 0x50, 0x11, 0xc0, 0x94, 0xa8, 0xaa, 0x50, 0x94, 0xc7, 0xb2, 0x4d, 0x7a, 0xab, 0xc7, 0x7d, 0x33, 0x2d, 0x62, 0x50, + 0xe2, 0xc5, 0x68, 0x91, 0x7a, 0x31, 0xce, 0x20, 0x1d, 0x91, 0x17, 0x25, 0xfc, 0xd4, 0x5e, 0x8d, 0x5a, 0xb2, 0xf2, + 0xe6, 0x33, 0x7e, 0xa0, 0xcc, 0x0b, 0x48, 0xd1, 0xc4, 0xa9, 0xe1, 0x29, 0xa9, 0x27, 0x0f, 0xdb, 0x59, 0xcb, 0xf6, + 0xb5, 0x4e, 0xd0, 0xd1, 0x80, 0xfd, 0x20, 0xbc, 0x85, 0x35, 0x0b, 0xfb, 0x34, 0xb7, 0x3e, 0xf3, 0xa7, 0x83, 0x7d, + 0x55, 0x0e, 0xa9, 0x97, 0x93, 0x15, 0x89, 0x73, 0x7f, 0xaa, 0xe5, 0xcf, 0x33, 0x9a, 0xdd, 0x9d, 0x53, 0x48, 0x75, + 0xe6, 0x70, 0xda, 0xb7, 0x5a, 0x86, 0x2a, 0x4d, 0xbd, 0x9f, 0x49, 0x65, 0xa5, 0xa8, 0x9f, 0x02, 0x5c, 0x3d, 0x23, + 0x58, 0xc8, 0x68, 0xa3, 0xe5, 0x88, 0x51, 0xbb, 0x85, 0x6e, 0x3d, 0x3d, 0x49, 0xbb, 0x0c, 0xfc, 0x6b, 0x15, 0xa6, + 0x75, 0xb0, 0x00, 0x73, 0xfb, 0x44, 0xea, 0x20, 0xbf, 0x58, 0xf5, 0xca, 0x40, 0x11, 0x84, 0xef, 0xb2, 0xed, 0x53, + 0xdd, 0x94, 0x34, 0xbb, 0x7d, 0xaa, 0xb5, 0xa0, 0x9f, 0x4c, 0xf8, 0xc1, 0x7a, 0x9c, 0xf2, 0xf8, 0x32, 0x2b, 0x0a, + 0x54, 0x00, 0x78, 0x7f, 0xed, 0x7a, 0xde, 0x5f, 0x75, 0xca, 0xa0, 0x0f, 0xb1, 0xd8, 0xf3, 0x84, 0x1b, 0x26, 0x5e, + 0x8d, 0xff, 0xd7, 0xb5, 0xf1, 0xff, 0x6a, 0x9d, 0x39, 0x05, 0xd3, 0x68, 0x9c, 0xd2, 0xc8, 0xb0, 0x4e, 0xa4, 0x08, + 0x50, 0xea, 0x6d, 0xa9, 0x20, 0x6f, 0xae, 0x02, 0xd0, 0xb8, 0x16, 0x23, 0x9e, 0x8a, 0xe6, 0x28, 0x9c, 0xb0, 0xe4, + 0x2e, 0x98, 0xb1, 0xe6, 0x84, 0xa7, 0x3c, 0x9f, 0x86, 0x43, 0x8a, 0xf3, 0xbb, 0x5c, 0xd0, 0x49, 0x73, 0xc6, 0xf0, + 0x0b, 0x9a, 0xcc, 0xa9, 0x60, 0xc3, 0x10, 0xbb, 0xa7, 0x19, 0x0b, 0x13, 0xe7, 0x75, 0x98, 0x65, 0xfc, 0xc6, 0xc5, + 0xef, 0xf8, 0x35, 0x17, 0x1c, 0xbf, 0xb9, 0xbd, 0x1b, 0xd3, 0x14, 0x7f, 0xb8, 0x9e, 0xa5, 0x62, 0x86, 0xf3, 0x30, + 0xcd, 0x9b, 0x39, 0xcd, 0xd8, 0xa8, 0x3b, 0xe4, 0x09, 0xcf, 0x9a, 0x90, 0xb1, 0x3d, 0xa1, 0x41, 0xc2, 0xc6, 0xb1, + 0x70, 0xa2, 0x30, 0xfb, 0xd8, 0x6d, 0x36, 0xa7, 0x19, 0x9b, 0x84, 0xd9, 0x5d, 0x53, 0xd6, 0x08, 0x3e, 0x6f, 0x1d, + 0x84, 0x4f, 0x46, 0x87, 0x5d, 0x91, 0x85, 0x69, 0xce, 0x60, 0x99, 0x82, 0x30, 0x49, 0x9c, 0x83, 0xa3, 0xd6, 0x24, + 0xdf, 0x51, 0x81, 0xbc, 0x30, 0x15, 0xc5, 0x15, 0x7e, 0x03, 0x70, 0xfb, 0xd7, 0x22, 0xc5, 0xd7, 0x33, 0x21, 0x78, + 0xba, 0x18, 0xce, 0xb2, 0x9c, 0x67, 0xc1, 0x94, 0xb3, 0x54, 0xd0, 0xac, 0x7b, 0xcd, 0xb3, 0x88, 0x66, 0xcd, 0x2c, + 0x8c, 0xd8, 0x2c, 0x0f, 0x0e, 0xa7, 0xb7, 0x5d, 0xd0, 0x2c, 0xc6, 0x19, 0x9f, 0xa5, 0x91, 0x1e, 0x8b, 0xa5, 0x31, + 0xcd, 0x98, 0xb0, 0x5f, 0xc8, 0x4b, 0x4c, 0x82, 0x84, 0xa5, 0x34, 0xcc, 0x9a, 0x63, 0x68, 0x0c, 0x66, 0x51, 0x2b, + 0xa2, 0x63, 0x9c, 0x8d, 0xaf, 0x43, 0xaf, 0xdd, 0x79, 0x8c, 0xcd, 0x5f, 0xff, 0x08, 0x39, 0xad, 0xcd, 0xc5, 0xed, + 0x56, 0xeb, 0x4f, 0xa8, 0xbb, 0x32, 0x8a, 0x04, 0x28, 0x68, 0x4f, 0x6f, 0x9d, 0x9c, 0x43, 0x46, 0xdb, 0xa6, 0x96, + 0xdd, 0x69, 0x18, 0x41, 0x3e, 0x70, 0xd0, 0x99, 0xde, 0x16, 0x30, 0xbb, 0x40, 0xa5, 0x98, 0xea, 0x49, 0xea, 0xa7, + 0xc5, 0x6f, 0x85, 0xf8, 0x78, 0x33, 0xc4, 0x1d, 0x03, 0x71, 0x85, 0xf5, 0x66, 0x34, 0xcb, 0x64, 0x6c, 0x35, 0x68, + 0xe7, 0x0a, 0x90, 0x98, 0xcf, 0x69, 0x66, 0xe0, 0x90, 0x0f, 0xbf, 0x19, 0x8c, 0xce, 0x66, 0x30, 0x8e, 0x3f, 0x05, + 0x46, 0x96, 0x46, 0x8b, 0xfa, 0xba, 0xb6, 0x33, 0x3a, 0xe9, 0xc6, 0x14, 0xe8, 0x29, 0xe8, 0xc0, 0xef, 0x1b, 0x16, + 0x89, 0x58, 0xfd, 0x94, 0xe4, 0x7c, 0xa3, 0xde, 0x1d, 0xb5, 0x5a, 0xea, 0x39, 0x67, 0xbf, 0xd0, 0xa0, 0xed, 0x43, + 0x85, 0xe2, 0x0a, 0xff, 0xad, 0x3c, 0xcb, 0x5b, 0xe7, 0x9e, 0xf8, 0x1b, 0xfb, 0x90, 0xaf, 0x95, 0xa2, 0x58, 0x1d, + 0x89, 0xc6, 0x99, 0x91, 0x95, 0x4a, 0xf8, 0x80, 0xdb, 0x4e, 0x72, 0x47, 0xc2, 0x7a, 0xe5, 0x21, 0x4e, 0xd6, 0xff, + 0x46, 0xe5, 0x5d, 0x04, 0x10, 0xe9, 0xb0, 0x52, 0x0d, 0x79, 0x37, 0xeb, 0x91, 0x56, 0x37, 0x6b, 0x36, 0x91, 0xc7, + 0x49, 0x3a, 0xc8, 0x74, 0x72, 0x9e, 0xc7, 0xfa, 0x5c, 0x1a, 0xdb, 0x39, 0x0a, 0x38, 0x9c, 0x34, 0x5d, 0x2e, 0xab, + 0x30, 0x00, 0x93, 0xa7, 0x35, 0xfe, 0x26, 0x74, 0x05, 0x9c, 0x5b, 0x9c, 0x9c, 0x9b, 0xab, 0x5d, 0x52, 0xc3, 0x2b, + 0x12, 0x3e, 0x94, 0x98, 0xf3, 0xa7, 0xa1, 0x88, 0xc1, 0x4b, 0x51, 0x8a, 0x9f, 0x2a, 0x85, 0xc9, 0xdd, 0x77, 0x51, + 0x3f, 0x2d, 0xf3, 0xdb, 0x20, 0x8f, 0x2f, 0x2d, 0xa0, 0x97, 0xef, 0x05, 0x81, 0x1e, 0xf1, 0x57, 0x44, 0xd9, 0x74, + 0xc6, 0xa2, 0x1b, 0x3d, 0xd4, 0xa2, 0xa3, 0xa9, 0x60, 0x32, 0x73, 0xdb, 0x44, 0x1c, 0xe2, 0x30, 0xbf, 0x1c, 0xaa, + 0xa3, 0x92, 0x79, 0x75, 0x30, 0x20, 0x94, 0xd0, 0x2b, 0x23, 0x8d, 0x66, 0xd2, 0x1e, 0xfd, 0xab, 0xd8, 0x6a, 0x9f, + 0xa4, 0xf7, 0xd9, 0x27, 0xe5, 0xc4, 0x73, 0x3e, 0xcb, 0x86, 0x10, 0x8e, 0xd4, 0x52, 0x6f, 0xdd, 0x71, 0xe3, 0x4a, + 0x15, 0xc3, 0xc5, 0xc2, 0xca, 0x03, 0x15, 0x98, 0xd9, 0xd7, 0x4a, 0x50, 0x19, 0xf2, 0x52, 0xc7, 0x35, 0xb4, 0x88, + 0x33, 0x53, 0x02, 0x99, 0x1d, 0xc9, 0x94, 0x46, 0x2f, 0x23, 0xbd, 0xcc, 0x9f, 0xa5, 0xec, 0xe7, 0x19, 0xbd, 0x64, + 0xa0, 0x6b, 0x32, 0x9f, 0x45, 0x32, 0xd6, 0x04, 0xb2, 0xaf, 0xd9, 0x86, 0xe0, 0x05, 0x8b, 0xd4, 0xc2, 0x64, 0xf2, + 0xa5, 0xce, 0x6d, 0x72, 0x9b, 0x2e, 0xf8, 0x8b, 0x41, 0x3b, 0x60, 0x38, 0xe2, 0x93, 0x90, 0xa5, 0x81, 0x74, 0xf9, + 0x96, 0x9d, 0x05, 0x50, 0x1b, 0xb3, 0x28, 0xc8, 0xf4, 0xf2, 0xb4, 0x91, 0xff, 0x13, 0x67, 0xa9, 0x6c, 0x5a, 0x74, + 0xb9, 0x44, 0xa8, 0x42, 0x1f, 0x31, 0x08, 0x3e, 0x55, 0x72, 0x8d, 0x23, 0x6c, 0xbf, 0x2e, 0x4f, 0x9d, 0xd7, 0x56, + 0xa0, 0xb5, 0xb2, 0x50, 0xca, 0x08, 0xe0, 0xab, 0xa5, 0x39, 0xcf, 0x84, 0xe7, 0xc5, 0x38, 0x41, 0xa4, 0x17, 0x4b, + 0x67, 0xd7, 0x49, 0x22, 0xff, 0xeb, 0x37, 0xdb, 0x41, 0xbb, 0x34, 0xdf, 0x6b, 0x87, 0x81, 0x55, 0x72, 0x94, 0x3e, + 0x50, 0x2a, 0xa7, 0x51, 0xfe, 0x56, 0x53, 0xad, 0x9e, 0xcb, 0xe9, 0x62, 0xbd, 0xdd, 0x94, 0xa8, 0xf2, 0x6a, 0x40, + 0xc8, 0x60, 0xd1, 0x96, 0xa1, 0x50, 0x51, 0xcd, 0xbb, 0x54, 0x25, 0xaf, 0x94, 0x88, 0xbe, 0xdc, 0x5d, 0xa4, 0x7a, + 0xc4, 0xe2, 0x8a, 0x19, 0x27, 0x53, 0x9d, 0xe4, 0x0a, 0x8d, 0x11, 0x4b, 0x0f, 0xdd, 0x54, 0x4d, 0xc1, 0x72, 0x47, + 0xd2, 0x8d, 0x74, 0xeb, 0xab, 0x47, 0xaa, 0x14, 0x84, 0xcd, 0x55, 0x64, 0xaa, 0xde, 0x26, 0xc0, 0xc0, 0x6c, 0xcd, + 0x85, 0x99, 0x02, 0x68, 0x63, 0x23, 0x0a, 0xe7, 0x68, 0xae, 0x76, 0x17, 0xdf, 0x8b, 0x62, 0xdf, 0xaa, 0x2a, 0x7f, + 0xb3, 0x08, 0xfe, 0x07, 0x09, 0xb8, 0x50, 0x4a, 0x69, 0xe0, 0xbe, 0x7d, 0x73, 0xfe, 0xde, 0xc5, 0x70, 0x3b, 0x17, + 0xcd, 0xf2, 0x60, 0xe1, 0xea, 0xd4, 0xb8, 0x26, 0x84, 0x59, 0xdd, 0xc0, 0x0d, 0xa7, 0x70, 0xd2, 0x58, 0xf2, 0x82, + 0xfd, 0xdb, 0xe6, 0xcd, 0xcd, 0x4d, 0x13, 0x0e, 0x42, 0x35, 0x67, 0x59, 0x42, 0xd3, 0x21, 0x8f, 0x68, 0xe4, 0x16, + 0x05, 0xf2, 0x45, 0x4c, 0xd3, 0xf2, 0xfe, 0x1e, 0x9e, 0x50, 0x3f, 0xe1, 0x63, 0x75, 0x88, 0x73, 0xd5, 0xaa, 0x1e, + 0x5e, 0x9d, 0xc8, 0x7b, 0xa9, 0x7a, 0x27, 0x42, 0xdd, 0x08, 0x26, 0x32, 0xf8, 0xd9, 0x83, 0x98, 0xcb, 0xc9, 0xbe, + 0x88, 0xe5, 0xc3, 0x39, 0xec, 0x30, 0xf9, 0xb4, 0xbb, 0x58, 0xa3, 0xbe, 0x3e, 0x74, 0x11, 0xf7, 0xd4, 0x9c, 0x73, + 0x59, 0xeb, 0x2a, 0x18, 0x5e, 0x5d, 0x15, 0x27, 0xfb, 0xd0, 0xd7, 0xbe, 0xe9, 0xf7, 0x9a, 0x47, 0x77, 0xa6, 0x7d, + 0x49, 0x91, 0x70, 0x3f, 0x51, 0x4a, 0x7a, 0xd0, 0x05, 0x8c, 0x1b, 0xf5, 0x00, 0x2b, 0x40, 0x91, 0xd0, 0x3a, 0x2a, + 0x4b, 0xe4, 0x16, 0x57, 0x45, 0xdb, 0x20, 0x50, 0x15, 0xab, 0x8d, 0xa2, 0xdc, 0xaf, 0x15, 0x41, 0x18, 0x90, 0x22, + 0x1b, 0xba, 0x2b, 0x04, 0xff, 0x4b, 0xc8, 0x4e, 0xf6, 0x15, 0x1e, 0xae, 0xec, 0xcb, 0x50, 0xd4, 0x35, 0x05, 0x25, + 0xb6, 0x06, 0xa9, 0xc0, 0x6f, 0x04, 0x7e, 0x73, 0x25, 0xab, 0x1a, 0xe9, 0x05, 0x6a, 0x15, 0x48, 0xf9, 0x96, 0x51, + 0x53, 0x86, 0x3c, 0x49, 0xc2, 0x69, 0x4e, 0x03, 0xf3, 0x43, 0x0b, 0x32, 0x90, 0x87, 0xeb, 0x9a, 0x83, 0xce, 0xc7, + 0x39, 0x03, 0xfd, 0x62, 0x5d, 0xad, 0x99, 0x87, 0x99, 0xd7, 0x6c, 0x0e, 0x9b, 0xd7, 0x63, 0x54, 0x88, 0x78, 0x61, + 0x8b, 0xc1, 0x47, 0xad, 0x56, 0x17, 0x92, 0x27, 0x9b, 0x61, 0xc2, 0xc6, 0x69, 0x90, 0xd0, 0x91, 0x28, 0x04, 0x9c, + 0x6a, 0x5b, 0x18, 0xbd, 0xc3, 0xef, 0x1c, 0x65, 0x74, 0xe2, 0xf8, 0xf0, 0xef, 0xfd, 0x03, 0x17, 0x22, 0x0a, 0x52, + 0x11, 0x37, 0x65, 0x92, 0x2e, 0x1c, 0x31, 0x10, 0x71, 0xed, 0x79, 0x61, 0x0d, 0x34, 0xa4, 0xa0, 0x93, 0x15, 0x22, + 0x73, 0x44, 0x8c, 0x45, 0x66, 0xd7, 0x4b, 0xd1, 0x62, 0x6d, 0x06, 0xeb, 0xaa, 0xc1, 0x01, 0x2a, 0x72, 0xa9, 0x49, + 0xaf, 0x57, 0x36, 0xfa, 0x55, 0xfd, 0x69, 0x0d, 0x7d, 0x96, 0x26, 0x58, 0x28, 0x4f, 0xf4, 0x42, 0xb5, 0x78, 0x08, + 0x32, 0x6b, 0x3a, 0x2a, 0xb6, 0x5b, 0xa0, 0x82, 0xa5, 0xd3, 0x99, 0x18, 0x48, 0x2f, 0x78, 0x06, 0xe7, 0x29, 0x2e, + 0xb0, 0x55, 0x02, 0x38, 0xb8, 0x58, 0x28, 0x60, 0x86, 0x61, 0x32, 0xf4, 0x00, 0x22, 0xa7, 0xe9, 0x1c, 0x67, 0x74, + 0x82, 0xba, 0x13, 0x96, 0x36, 0xd5, 0xbb, 0x23, 0x4b, 0x8f, 0xf1, 0x1f, 0xc3, 0x53, 0xe1, 0xcb, 0xde, 0xb0, 0x4c, + 0x76, 0xdd, 0x80, 0xcb, 0xab, 0x8b, 0xa2, 0xe8, 0x66, 0xc2, 0x1b, 0xbc, 0xf2, 0xd0, 0x05, 0xfe, 0xca, 0xba, 0xce, + 0xc5, 0x35, 0x5b, 0xc5, 0xc5, 0x1d, 0xb4, 0xa5, 0x8a, 0xbd, 0x17, 0x64, 0xb5, 0xaf, 0x08, 0x54, 0x7c, 0xea, 0xb9, + 0x34, 0x9f, 0x36, 0x15, 0xb3, 0x6b, 0x4a, 0x92, 0x75, 0xa1, 0x29, 0xd2, 0xae, 0xdd, 0xbf, 0x8a, 0x85, 0xe4, 0x63, + 0xfa, 0x4c, 0x87, 0xf2, 0x3e, 0x5c, 0x94, 0x67, 0x80, 0xf4, 0xb3, 0x7d, 0xea, 0x07, 0xd5, 0xf8, 0xc9, 0xd5, 0x69, + 0x9d, 0x29, 0x02, 0x23, 0x2b, 0xef, 0xbc, 0x0b, 0x93, 0x04, 0x06, 0xbc, 0x32, 0xfa, 0x8e, 0x7d, 0x49, 0xc8, 0x40, + 0x5c, 0x78, 0xa8, 0xd0, 0xfb, 0xf4, 0xa9, 0xd4, 0x41, 0xad, 0x8b, 0xf6, 0x76, 0x84, 0x89, 0x2e, 0x29, 0x71, 0xcd, + 0x20, 0x3e, 0x5e, 0xcb, 0xa3, 0xee, 0x56, 0xbc, 0x4b, 0x69, 0xb0, 0x8e, 0x9c, 0x10, 0x71, 0xb3, 0x34, 0x72, 0x9d, + 0xbf, 0x0c, 0x13, 0x36, 0xfc, 0x48, 0xdc, 0xdd, 0x85, 0x87, 0xd6, 0x8f, 0x49, 0x4a, 0xae, 0x60, 0x38, 0x3c, 0xaa, + 0x7b, 0xde, 0x33, 0xdf, 0x62, 0xde, 0xea, 0x1e, 0x1d, 0xb7, 0xb7, 0xbb, 0x00, 0xc6, 0xa3, 0xc6, 0xe9, 0x5d, 0x15, + 0x97, 0xd5, 0xf5, 0x58, 0x15, 0x14, 0x80, 0x66, 0x55, 0xee, 0x48, 0xa2, 0x22, 0xee, 0x27, 0x29, 0xcd, 0x75, 0x14, + 0x53, 0x03, 0x38, 0x85, 0xe6, 0x6f, 0xae, 0xf3, 0x97, 0xb2, 0x8c, 0x96, 0x2e, 0x10, 0x99, 0xc3, 0x41, 0x5c, 0x18, + 0x0b, 0xec, 0x5e, 0x3f, 0xa2, 0x22, 0x64, 0x89, 0x6a, 0xd2, 0x35, 0x16, 0xfb, 0xca, 0x8c, 0x96, 0xcb, 0xbc, 0x3e, + 0x17, 0x56, 0xc7, 0xa0, 0x9c, 0xd9, 0xc9, 0x7e, 0x05, 0xb7, 0x9c, 0x99, 0xdc, 0x93, 0x76, 0x2c, 0xb1, 0x9a, 0xa1, + 0x7a, 0xe7, 0xfc, 0x65, 0x28, 0x4f, 0x19, 0x01, 0x80, 0x5c, 0x03, 0x08, 0x51, 0x6e, 0x75, 0x8a, 0xc6, 0x4b, 0x08, + 0xf7, 0x45, 0x98, 0x8d, 0xa9, 0x58, 0x41, 0x6c, 0xa2, 0x92, 0x5a, 0xbb, 0x26, 0xa2, 0xbd, 0x06, 0x6d, 0x58, 0x87, + 0xf6, 0x0a, 0x90, 0xde, 0xdf, 0x5d, 0xb0, 0x82, 0xec, 0x2e, 0x94, 0x5c, 0xfb, 0xf0, 0xee, 0x2b, 0x38, 0x14, 0xc9, + 0x53, 0xb0, 0x44, 0x62, 0x04, 0x92, 0x56, 0x2e, 0x8e, 0x12, 0x21, 0x5c, 0x8a, 0x10, 0xc5, 0x09, 0x1c, 0x39, 0x96, + 0x04, 0xb1, 0x70, 0x9d, 0xbe, 0x82, 0x9c, 0x46, 0x0a, 0x66, 0x92, 0xc9, 0x56, 0xbc, 0x38, 0xd9, 0x57, 0xb5, 0x95, + 0x08, 0x50, 0x95, 0x00, 0x09, 0x72, 0x9f, 0x56, 0x38, 0x80, 0x44, 0x68, 0x1b, 0x0f, 0x11, 0x9b, 0x97, 0xc4, 0x26, + 0xcf, 0x5b, 0xf5, 0x4e, 0x92, 0xf0, 0x9a, 0x26, 0xbd, 0xdd, 0x45, 0xb6, 0x5c, 0xb6, 0x8a, 0x93, 0x7d, 0xf5, 0xe8, + 0x9c, 0x48, 0xbe, 0xa1, 0xee, 0xc8, 0x94, 0x4b, 0x0c, 0x87, 0x18, 0x21, 0x3d, 0xd4, 0xe4, 0x45, 0x05, 0xba, 0x83, + 0xc2, 0x75, 0x64, 0x46, 0x86, 0xac, 0x54, 0x6a, 0x50, 0x85, 0xeb, 0xb0, 0x68, 0xbd, 0x2c, 0x17, 0x74, 0x0a, 0xa5, + 0xf1, 0x72, 0xd9, 0x2e, 0x5c, 0x67, 0xc2, 0x52, 0x78, 0xca, 0x96, 0x4b, 0x79, 0x3e, 0x70, 0xc2, 0x52, 0xaf, 0x05, + 0x64, 0xeb, 0x3a, 0x93, 0xf0, 0x56, 0x4e, 0xd8, 0xbc, 0x09, 0x6f, 0xbd, 0xb6, 0x7e, 0xe5, 0x97, 0xf8, 0xc9, 0x81, + 0xe2, 0xaa, 0x15, 0x4d, 0xf4, 0x8a, 0x46, 0x78, 0xa6, 0x4e, 0x3e, 0x11, 0x2f, 0x22, 0xc9, 0xe6, 0x15, 0x8d, 0xcc, + 0x8a, 0xce, 0xb6, 0xac, 0xe8, 0xec, 0x9e, 0x15, 0x0d, 0xf5, 0xea, 0x39, 0x25, 0xee, 0xf8, 0x72, 0xd9, 0x6e, 0x55, + 0xd8, 0x3b, 0xd9, 0x8f, 0xd8, 0x1c, 0x56, 0x03, 0xf4, 0x42, 0xc1, 0x26, 0x74, 0x33, 0x51, 0xd6, 0x51, 0x4c, 0x7f, + 0x15, 0x26, 0x2b, 0x2c, 0x64, 0x75, 0x2c, 0xd8, 0x74, 0x5d, 0x06, 0xe9, 0xfe, 0x48, 0xca, 0x66, 0x80, 0x87, 0x1c, + 0xf0, 0x10, 0x9b, 0x3b, 0x33, 0x3d, 0xf7, 0xbd, 0x8b, 0x5d, 0xc7, 0x35, 0x64, 0x7d, 0x55, 0x5c, 0x82, 0x8c, 0x90, + 0xf3, 0x7b, 0x10, 0x2d, 0x42, 0x6d, 0xb7, 0xb7, 0x9d, 0xe6, 0x20, 0x9e, 0x7e, 0xc3, 0xb3, 0xc8, 0x0d, 0x54, 0xd5, + 0x5f, 0x85, 0xaa, 0x09, 0x4b, 0x75, 0x76, 0xd6, 0x56, 0x5a, 0xab, 0xde, 0xdb, 0x14, 0xd7, 0x39, 0x3a, 0x52, 0x35, + 0xa6, 0xa1, 0x10, 0x34, 0x4b, 0x35, 0xe5, 0xba, 0xee, 0xff, 0x17, 0x54, 0xb8, 0x81, 0xaf, 0x84, 0x66, 0x01, 0x0c, + 0x01, 0x6a, 0x0d, 0x5f, 0xf3, 0x7c, 0x25, 0x9e, 0x76, 0x2a, 0x0d, 0xf6, 0x0e, 0xd9, 0x56, 0x86, 0x2a, 0x02, 0xa3, + 0x67, 0x36, 0xa1, 0xd1, 0xa5, 0x64, 0xd0, 0xfd, 0xe1, 0x95, 0x56, 0x58, 0x57, 0xc4, 0x5d, 0xd5, 0x00, 0xbb, 0x3f, + 0xce, 0x3a, 0x8f, 0x0f, 0xcf, 0x5c, 0xac, 0x78, 0x3c, 0x1f, 0x8d, 0x5c, 0x54, 0x38, 0x0f, 0x6b, 0xd6, 0x3e, 0xfc, + 0x71, 0xf6, 0xe5, 0xf3, 0xd6, 0x97, 0x65, 0xe3, 0x14, 0x88, 0x48, 0x27, 0x04, 0x18, 0x51, 0x65, 0xc1, 0x6b, 0x66, + 0x34, 0x0a, 0xd3, 0xed, 0xd3, 0x19, 0xd8, 0xd3, 0xc9, 0xa7, 0x94, 0x46, 0x40, 0x9c, 0x78, 0xad, 0xf4, 0x32, 0xa1, + 0x73, 0x6a, 0xee, 0x2a, 0xdc, 0x30, 0xd8, 0x86, 0x16, 0x43, 0x3e, 0x4b, 0x85, 0xce, 0x8c, 0xd0, 0xac, 0xd6, 0x9a, + 0xd2, 0x95, 0x9c, 0x83, 0x6d, 0x23, 0xdc, 0x29, 0x39, 0x57, 0x97, 0x5e, 0xc5, 0x15, 0x76, 0x2d, 0x00, 0xb6, 0x42, + 0xd6, 0xdf, 0x52, 0x1e, 0xb4, 0x70, 0x6b, 0x1b, 0x6c, 0xb8, 0x8d, 0x02, 0xd7, 0xbd, 0x30, 0x78, 0x92, 0xce, 0xcd, + 0xda, 0x05, 0x13, 0x5b, 0xf1, 0xf5, 0x49, 0x0c, 0x5c, 0x67, 0xd0, 0x59, 0x4a, 0xf3, 0x7c, 0x2b, 0x02, 0xca, 0x45, + 0xc4, 0x6e, 0x55, 0xdb, 0xdd, 0xd2, 0x0b, 0x6e, 0x61, 0xd8, 0x61, 0x12, 0xe0, 0x32, 0xc4, 0xaa, 0x6b, 0xd1, 0xd1, + 0x88, 0x0e, 0x4b, 0xdf, 0x30, 0x04, 0xcb, 0x46, 0x2c, 0x11, 0x10, 0x33, 0x92, 0xc1, 0x1c, 0xf7, 0x35, 0x4f, 0xa9, + 0x8b, 0x4c, 0xfa, 0xa7, 0x86, 0x5f, 0xcb, 0xff, 0xcd, 0xf0, 0xa8, 0x1e, 0xeb, 0xb0, 0xe8, 0x51, 0x96, 0x4b, 0xe3, + 0x17, 0xaa, 0x95, 0xd7, 0x11, 0xc9, 0xa5, 0xe3, 0x67, 0xdb, 0x06, 0x7a, 0xd8, 0x36, 0x59, 0xb4, 0xbf, 0x3c, 0x6a, + 0xb7, 0x0a, 0x17, 0xbb, 0xd0, 0xdd, 0x43, 0x77, 0x89, 0x6c, 0x75, 0x00, 0xad, 0x66, 0xe9, 0xaf, 0x69, 0xd7, 0x69, + 0x3f, 0x69, 0xbb, 0x58, 0xdd, 0x3b, 0x80, 0x8a, 0x92, 0x19, 0x0c, 0xc1, 0x5b, 0xfa, 0xbb, 0xa7, 0x52, 0xef, 0xfc, + 0x61, 0xf0, 0x3c, 0x6a, 0xb7, 0x5c, 0xec, 0xe6, 0x82, 0x4f, 0x7f, 0xc5, 0x14, 0x0e, 0x5c, 0xec, 0x0e, 0x13, 0x9e, + 0x53, 0x7b, 0x0e, 0x4a, 0x9d, 0xfd, 0xfd, 0x93, 0x50, 0x10, 0x4d, 0x33, 0x9a, 0xe7, 0x8e, 0xdd, 0xbf, 0x26, 0xa5, + 0x4f, 0x30, 0xcc, 0x8d, 0x14, 0x97, 0x53, 0x21, 0xf1, 0xa2, 0xae, 0x04, 0xb0, 0xa9, 0x4a, 0x95, 0xad, 0x11, 0x9b, + 0x14, 0x01, 0x25, 0x63, 0x53, 0xda, 0xd5, 0x27, 0x47, 0xde, 0xb0, 0xf5, 0xd4, 0xc0, 0x2a, 0x88, 0xbc, 0x3e, 0x40, + 0xad, 0x64, 0xc2, 0xd2, 0xcb, 0x0d, 0xa5, 0xe1, 0xed, 0x86, 0x52, 0x50, 0xd9, 0x4a, 0xe8, 0xf4, 0x75, 0x35, 0x9f, + 0xc6, 0x7a, 0xa5, 0xf8, 0xd8, 0x20, 0x46, 0xd2, 0xd1, 0xf9, 0x09, 0x48, 0xad, 0x65, 0x90, 0x3d, 0xfc, 0xf6, 0xe1, + 0xa0, 0xe4, 0xd7, 0x0c, 0x57, 0xf6, 0xf2, 0xfb, 0x66, 0x08, 0xa5, 0x4d, 0x70, 0x78, 0x27, 0xbf, 0x6a, 0xae, 0xf4, + 0xf6, 0xd3, 0x04, 0x67, 0x69, 0x55, 0xbf, 0x63, 0xe9, 0xf5, 0xb1, 0xf7, 0xd5, 0xb5, 0xdf, 0x50, 0xac, 0x15, 0x9f, + 0x72, 0xfd, 0x87, 0x09, 0x9b, 0x54, 0x24, 0xb0, 0x0e, 0xa6, 0xd4, 0x78, 0x20, 0xfb, 0xc9, 0xee, 0x44, 0xa9, 0x3e, + 0x97, 0x70, 0xa6, 0x13, 0xae, 0xcd, 0x98, 0x65, 0xf4, 0x32, 0xe1, 0x37, 0xab, 0xf7, 0x80, 0x6d, 0xaf, 0x1c, 0xb3, + 0x71, 0x6c, 0x1d, 0xd4, 0xa2, 0xa4, 0x5c, 0x84, 0x7b, 0x07, 0x28, 0xfe, 0xe5, 0x9f, 0x7d, 0xff, 0x5f, 0xfe, 0xf9, + 0x93, 0x55, 0xa1, 0xfb, 0xe2, 0x0a, 0x8b, 0xaa, 0xdb, 0xed, 0xbb, 0x6b, 0xf3, 0x48, 0x75, 0x9c, 0x6f, 0xae, 0xb3, + 0xb6, 0x08, 0xf0, 0x7e, 0x6d, 0x09, 0xd6, 0x0a, 0xd5, 0xee, 0x73, 0x7e, 0x0b, 0x60, 0x30, 0xaf, 0x4f, 0x42, 0x06, + 0x95, 0x7e, 0x17, 0x68, 0x57, 0x28, 0x78, 0xd0, 0x8a, 0xfc, 0x76, 0x0c, 0x7f, 0x6a, 0x0e, 0xbf, 0x13, 0x7c, 0xed, + 0x9f, 0x18, 0x5e, 0x5d, 0x95, 0x19, 0x79, 0x76, 0x53, 0x38, 0xef, 0xdf, 0x5f, 0x2b, 0xd1, 0x8a, 0x47, 0xd0, 0x42, + 0x3d, 0x79, 0x9e, 0x90, 0x0c, 0xaf, 0x5e, 0xc1, 0x25, 0x3f, 0x27, 0xd7, 0x99, 0x71, 0xf0, 0xde, 0x23, 0x1c, 0xa0, + 0x8b, 0xfa, 0xac, 0x64, 0xa7, 0x6b, 0x92, 0x01, 0x4a, 0xc1, 0xdc, 0x00, 0x30, 0xf1, 0xf0, 0x4a, 0x5b, 0x9b, 0x67, + 0xca, 0x0d, 0x13, 0xac, 0x92, 0xb6, 0x76, 0xcf, 0xd4, 0x90, 0x8e, 0x9d, 0xf7, 0x12, 0x5f, 0xb2, 0x32, 0xad, 0xac, + 0x7b, 0xe9, 0xea, 0x02, 0x3b, 0xa2, 0x64, 0x3f, 0xf3, 0x30, 0x99, 0x3f, 0x8c, 0xf1, 0x6d, 0x17, 0xa8, 0x4b, 0x67, + 0xf9, 0x6f, 0xad, 0x12, 0x2c, 0x9b, 0xcb, 0x9a, 0x3e, 0x20, 0xb3, 0x12, 0xfe, 0xbe, 0x2d, 0x70, 0x2a, 0xe8, 0x27, + 0x03, 0xa7, 0xc9, 0x83, 0x02, 0xa7, 0xea, 0x86, 0xbe, 0x3f, 0x32, 0x70, 0xfa, 0x77, 0x3b, 0x70, 0x0a, 0x24, 0xf8, + 0xf3, 0x83, 0x82, 0x9b, 0x26, 0xf0, 0xc4, 0x6f, 0x72, 0xd2, 0xd6, 0x46, 0x40, 0xc2, 0xc7, 0x10, 0xd9, 0xfc, 0xb7, + 0x0f, 0x54, 0x26, 0x7c, 0x6c, 0x87, 0x29, 0xe1, 0x8e, 0x5a, 0x88, 0x4b, 0xe2, 0x8c, 0x2c, 0xdc, 0x1f, 0x6f, 0xdb, + 0x4f, 0x07, 0xed, 0xee, 0x41, 0x7b, 0xe2, 0x06, 0x2e, 0x48, 0x5d, 0x59, 0xd0, 0xea, 0x1e, 0x1c, 0x40, 0xc1, 0x8d, + 0x55, 0xd0, 0x81, 0x02, 0x66, 0x15, 0x1c, 0x41, 0xc1, 0xd0, 0x2a, 0x78, 0x04, 0x05, 0x91, 0x55, 0xf0, 0x18, 0x0a, + 0xe6, 0x6e, 0x31, 0x60, 0x65, 0x74, 0xf8, 0x31, 0x92, 0xd7, 0x59, 0xec, 0x64, 0xf5, 0x54, 0xfe, 0x98, 0x98, 0x2a, + 0x8f, 0xcb, 0x63, 0x40, 0xcd, 0x43, 0x73, 0x6b, 0xc5, 0xd5, 0x67, 0x57, 0x08, 0x27, 0x04, 0x4e, 0xe5, 0x61, 0x30, + 0xca, 0x55, 0xcd, 0x03, 0xf3, 0xda, 0x0d, 0xca, 0x7b, 0xa9, 0x5a, 0xb8, 0x63, 0x22, 0x9c, 0x81, 0x8b, 0xf0, 0xac, + 0xac, 0x7c, 0xd4, 0x88, 0x74, 0xb7, 0x70, 0x21, 0x44, 0x75, 0x1b, 0xcb, 0x01, 0xc2, 0xea, 0x02, 0xec, 0x67, 0x52, + 0x3e, 0xfa, 0x82, 0xbf, 0x67, 0x13, 0x6a, 0x3e, 0x0f, 0x62, 0x06, 0x70, 0x5c, 0x04, 0x07, 0xb8, 0xe3, 0xea, 0x0a, + 0xb3, 0x2f, 0xf1, 0x69, 0x75, 0x01, 0xd0, 0x5b, 0x41, 0xd4, 0x8d, 0x0a, 0x19, 0x56, 0x86, 0xde, 0x18, 0x8b, 0x70, + 0x1c, 0x40, 0xc8, 0x12, 0x7c, 0xa6, 0xc1, 0x29, 0x21, 0xa4, 0xd5, 0x9f, 0x05, 0x5f, 0xe2, 0x9b, 0x98, 0xa6, 0xc1, + 0xbc, 0xe8, 0x96, 0x04, 0xa0, 0x22, 0xa6, 0x6f, 0x45, 0x79, 0x6f, 0x9c, 0xa4, 0x8a, 0xea, 0xb5, 0x82, 0xb3, 0x59, + 0x52, 0xcf, 0x96, 0x58, 0x9a, 0xe5, 0x93, 0x19, 0x25, 0xfc, 0xa6, 0x79, 0xeb, 0xf6, 0x36, 0xc7, 0xd7, 0x60, 0x76, + 0x65, 0x7c, 0x4d, 0x02, 0x5b, 0x3e, 0xbd, 0x0f, 0xc7, 0xe5, 0xef, 0x57, 0x34, 0xcf, 0xc3, 0xb1, 0xae, 0xb9, 0x3d, + 0x9e, 0x26, 0x41, 0xb4, 0x63, 0x69, 0x06, 0x08, 0x88, 0x89, 0x01, 0x46, 0xc0, 0xa7, 0xa1, 0x43, 0x64, 0x30, 0xf5, + 0x7a, 0x74, 0x4d, 0x0e, 0x5f, 0x2f, 0x12, 0xe1, 0xb8, 0x2a, 0x38, 0x99, 0x66, 0x54, 0x96, 0x2a, 0x34, 0x16, 0x27, + 0xfb, 0x50, 0xa0, 0x5e, 0x6f, 0x89, 0xa2, 0x19, 0x07, 0xca, 0xf6, 0x58, 0x9a, 0x63, 0xa2, 0x68, 0x76, 0xa2, 0x52, + 0x99, 0xa5, 0xb4, 0x1e, 0xbb, 0xf9, 0xbc, 0x3d, 0x84, 0x3f, 0x3a, 0x32, 0xf4, 0xf9, 0x68, 0x34, 0xba, 0x37, 0xaa, + 0xf6, 0x79, 0x34, 0xa2, 0x1d, 0x7a, 0xd4, 0x85, 0x24, 0x96, 0xa6, 0x8e, 0xc5, 0xb4, 0x0b, 0x89, 0xbb, 0xc5, 0xc3, + 0x2a, 0x43, 0xd8, 0x46, 0xc4, 0x8b, 0x87, 0x47, 0xd8, 0x8a, 0x69, 0x46, 0x17, 0x93, 0x30, 0x1b, 0xb3, 0x34, 0x68, + 0x15, 0xfe, 0x5c, 0x87, 0xa4, 0x3e, 0x3f, 0x3e, 0x3e, 0x2e, 0xfc, 0xc8, 0x3c, 0xb5, 0xa2, 0xa8, 0xf0, 0x87, 0x8b, + 0x72, 0x1a, 0xad, 0xd6, 0x68, 0x54, 0xf8, 0xcc, 0x14, 0x1c, 0x74, 0x86, 0xd1, 0x41, 0xa7, 0xf0, 0x6f, 0xac, 0x1a, + 0x85, 0x4f, 0xf5, 0x53, 0x46, 0xa3, 0x5a, 0x26, 0xcc, 0xe3, 0x56, 0xab, 0xf0, 0x15, 0xa1, 0x2d, 0xc0, 0x2c, 0x55, + 0x3f, 0x83, 0x70, 0x26, 0x38, 0x30, 0xf7, 0x6e, 0x22, 0xbc, 0xc1, 0xa5, 0xbe, 0x65, 0x44, 0x7d, 0x93, 0xa3, 0x40, + 0x17, 0xf8, 0x67, 0x3b, 0x78, 0x04, 0xc4, 0x2c, 0x83, 0x46, 0x89, 0x89, 0x2d, 0xd5, 0x5e, 0x03, 0x65, 0xc9, 0xd7, + 0x3f, 0x93, 0xa4, 0x8a, 0x29, 0x01, 0x27, 0x83, 0x9a, 0xea, 0x32, 0x3c, 0x4a, 0xb7, 0xc8, 0x0f, 0xf6, 0x69, 0xf9, + 0x71, 0xf7, 0x10, 0xf1, 0xc1, 0xfe, 0x70, 0xf1, 0x41, 0xa9, 0x25, 0x3e, 0x14, 0xf3, 0xb8, 0x13, 0xc4, 0x1d, 0xc6, + 0x74, 0xf8, 0xf1, 0x9a, 0xdf, 0x36, 0x61, 0x4b, 0x64, 0xae, 0x14, 0x2c, 0xbb, 0xbf, 0x35, 0x6b, 0xc6, 0x74, 0x66, + 0x7d, 0xd1, 0x43, 0xaa, 0x0f, 0x6f, 0x52, 0xe2, 0xbe, 0x31, 0xb6, 0xad, 0x2a, 0x19, 0x8d, 0x88, 0xfb, 0x66, 0x34, + 0x72, 0xcd, 0x59, 0xc9, 0x50, 0x50, 0x59, 0xeb, 0x75, 0xad, 0x44, 0xd6, 0xfa, 0xf2, 0x4b, 0xbb, 0xcc, 0x2e, 0xd0, + 0xa1, 0x27, 0x3b, 0xcc, 0xa4, 0xdf, 0x44, 0x2c, 0x87, 0xad, 0x06, 0x1f, 0x1a, 0xa9, 0xdf, 0xd5, 0x98, 0xd6, 0xae, + 0xd5, 0x2e, 0x01, 0xde, 0x70, 0x17, 0xf8, 0xea, 0x45, 0x01, 0x63, 0x6a, 0xf2, 0x16, 0x9f, 0xde, 0x7d, 0x15, 0x79, + 0x77, 0x02, 0x15, 0x2c, 0x7f, 0x93, 0xae, 0x1c, 0x02, 0x52, 0x30, 0x12, 0x62, 0x4f, 0xab, 0x10, 0x7c, 0x3c, 0x4e, + 0xe0, 0x5b, 0x2f, 0x8b, 0xda, 0xfd, 0xb1, 0xaa, 0x79, 0xbf, 0x36, 0xdf, 0xc0, 0x6e, 0xa8, 0x6f, 0x5b, 0x95, 0x9f, + 0x9e, 0x52, 0xc9, 0xe3, 0x73, 0xfd, 0x0d, 0x22, 0x69, 0x16, 0x2f, 0x34, 0x93, 0x5f, 0xa8, 0x94, 0x63, 0x01, 0xe9, + 0x36, 0xaa, 0xe3, 0xa8, 0x28, 0xf4, 0x61, 0x8d, 0x88, 0xe5, 0x53, 0xb8, 0xd7, 0x54, 0xb5, 0xa4, 0x9f, 0x62, 0xe1, + 0xf9, 0x8d, 0x15, 0xdf, 0xa9, 0x2d, 0x57, 0x61, 0x02, 0x3c, 0xca, 0x61, 0x7e, 0x27, 0x0a, 0x57, 0xfb, 0xdd, 0x0d, + 0x12, 0x5d, 0x47, 0xe1, 0x53, 0x45, 0x9e, 0xac, 0x19, 0x82, 0xf3, 0xbb, 0x5c, 0x10, 0xf3, 0xca, 0x14, 0x14, 0x76, + 0xfc, 0x52, 0xbe, 0x51, 0xd8, 0x92, 0xd1, 0x92, 0x7c, 0x1a, 0xa6, 0x8a, 0x8d, 0x12, 0x57, 0xf1, 0x83, 0xdd, 0x45, + 0xb5, 0xf2, 0x85, 0x6b, 0xc0, 0x56, 0xc4, 0xdb, 0x3b, 0xd9, 0x87, 0x06, 0x3d, 0xa7, 0x06, 0x7a, 0xba, 0x16, 0x64, + 0xf9, 0x44, 0xba, 0xc3, 0x95, 0x9f, 0xdf, 0x60, 0x3f, 0xbf, 0x71, 0xfe, 0xbc, 0x68, 0xde, 0xd0, 0xeb, 0x8f, 0x4c, + 0x34, 0x45, 0x38, 0x6d, 0x82, 0xe1, 0x23, 0x9d, 0xa3, 0x9a, 0x3d, 0xcb, 0x2c, 0x3f, 0x75, 0xd5, 0x41, 0x77, 0x96, + 0x43, 0x56, 0x84, 0x54, 0xdf, 0x83, 0x94, 0xa7, 0xb4, 0x5b, 0xcf, 0xe6, 0xb4, 0x83, 0xec, 0x06, 0x5b, 0x17, 0x0b, + 0x0e, 0x59, 0x14, 0xe2, 0x2e, 0x68, 0x69, 0xb6, 0xde, 0x32, 0x11, 0xf4, 0xd6, 0xc6, 0xfa, 0x81, 0x46, 0x6e, 0x43, + 0x4a, 0xaf, 0x6c, 0x3d, 0x93, 0x60, 0x5b, 0x26, 0xc0, 0xa7, 0x72, 0x1b, 0xc1, 0xa5, 0x6a, 0xfe, 0x5a, 0x49, 0xa1, + 0xab, 0xc5, 0x32, 0xb7, 0xf1, 0x21, 0x90, 0x05, 0xe1, 0x48, 0xd0, 0x0c, 0x3f, 0xa4, 0xe6, 0xb5, 0x3c, 0x86, 0xb4, + 0x00, 0x31, 0x13, 0xb4, 0x8f, 0xa7, 0xb7, 0x0f, 0xef, 0xfe, 0xfe, 0xe9, 0x17, 0x1a, 0x47, 0xe6, 0x5a, 0x1e, 0xd7, + 0xed, 0xc2, 0x46, 0x48, 0xc2, 0xbb, 0x80, 0xa5, 0x52, 0xe6, 0x5d, 0x83, 0x5f, 0xb4, 0x3b, 0xe5, 0x3a, 0x49, 0x37, + 0xa3, 0x89, 0xfc, 0x0a, 0x9f, 0x5e, 0x8a, 0x83, 0x47, 0xd3, 0x5b, 0xb3, 0x1a, 0xed, 0x95, 0xe4, 0xdb, 0x3f, 0x34, + 0xc7, 0x76, 0x7b, 0x52, 0x6f, 0x3d, 0x4f, 0xf4, 0x68, 0x7a, 0xdb, 0x55, 0x82, 0xb6, 0x99, 0x29, 0xa8, 0x5a, 0xd3, + 0x5b, 0x3b, 0xcb, 0xb8, 0xea, 0xc8, 0xf1, 0x0f, 0x72, 0x87, 0x86, 0x39, 0xed, 0xc2, 0xbd, 0xe3, 0x6c, 0x18, 0x26, + 0x5a, 0x98, 0x4f, 0x58, 0x14, 0x25, 0xb4, 0x6b, 0xe4, 0xb5, 0xd3, 0x7e, 0x04, 0x49, 0xba, 0xf6, 0x92, 0xd5, 0x57, + 0xc5, 0x42, 0x5e, 0x89, 0xa7, 0xf0, 0x3a, 0xe7, 0x09, 0x7c, 0xf4, 0x63, 0x23, 0x3a, 0x75, 0xf6, 0x6a, 0xab, 0x42, + 0x9e, 0xfc, 0x5d, 0x9f, 0xcb, 0x51, 0xeb, 0x4f, 0x5d, 0xb9, 0xe0, 0xad, 0xae, 0xe0, 0xd3, 0xa0, 0x79, 0x50, 0x9f, + 0x08, 0xbc, 0x2a, 0xa7, 0x80, 0x37, 0x4c, 0x0b, 0x83, 0xb4, 0x52, 0x7c, 0xda, 0xf1, 0xdb, 0xba, 0x4c, 0x76, 0x00, + 0x79, 0x61, 0x65, 0x51, 0x51, 0x9f, 0xcc, 0xbf, 0xcd, 0x6e, 0x79, 0xb2, 0x79, 0xb7, 0x3c, 0x31, 0xbb, 0xe5, 0x7e, + 0x8a, 0xfd, 0x7c, 0xd4, 0x86, 0x3f, 0xdd, 0x6a, 0x42, 0x41, 0xcb, 0x39, 0x98, 0xde, 0x3a, 0xa0, 0xa7, 0x35, 0x3b, + 0xd3, 0x5b, 0x95, 0x63, 0x0d, 0xb1, 0x9b, 0x16, 0x64, 0x1d, 0xe3, 0x96, 0x03, 0x85, 0xf0, 0xb7, 0x55, 0x7b, 0xd5, + 0x3e, 0x84, 0x77, 0xd0, 0xea, 0x68, 0xfd, 0x5d, 0xe7, 0xfe, 0x4d, 0x1b, 0xa4, 0x5c, 0x78, 0x81, 0xe1, 0xc6, 0xc8, + 0x17, 0xe1, 0xf5, 0x35, 0x8d, 0x82, 0x11, 0x1f, 0xce, 0xf2, 0x7f, 0xd2, 0xf0, 0x6b, 0x24, 0xde, 0xbb, 0xa5, 0x57, + 0xfa, 0x31, 0x4d, 0x55, 0xc6, 0xb7, 0xe9, 0x61, 0x51, 0xae, 0x53, 0x90, 0x0f, 0xc3, 0x84, 0x7a, 0x1d, 0xff, 0x70, + 0xc3, 0x26, 0xf8, 0x77, 0x59, 0x9b, 0x8d, 0x93, 0xf9, 0xbd, 0xc8, 0xb8, 0x17, 0x09, 0xbf, 0x0a, 0x07, 0xf6, 0x1a, + 0xb6, 0x8e, 0x37, 0x83, 0x3b, 0x30, 0x23, 0x5d, 0x18, 0xa1, 0xa0, 0xe5, 0x4e, 0x44, 0x47, 0xe1, 0x2c, 0x11, 0xf7, + 0xf7, 0xba, 0x8d, 0x32, 0xd6, 0x7a, 0xbd, 0x87, 0xa1, 0x57, 0x75, 0x1f, 0xc8, 0xa5, 0x3f, 0x7f, 0x72, 0x08, 0x7f, + 0x54, 0xfe, 0xd7, 0x5d, 0xa5, 0xab, 0x2b, 0xbb, 0x17, 0x74, 0xf5, 0xdd, 0x9a, 0x32, 0xae, 0x44, 0xb8, 0xd4, 0xc7, + 0x1f, 0x5a, 0x1b, 0xb4, 0xca, 0x07, 0x55, 0xd7, 0x5a, 0xd6, 0xaf, 0xaa, 0xfd, 0xeb, 0x3a, 0x7f, 0x60, 0xdd, 0xa1, + 0xd2, 0x5c, 0xeb, 0x75, 0xf5, 0x67, 0x08, 0xd7, 0x2a, 0x1b, 0x8c, 0xcb, 0xfa, 0xbb, 0xe4, 0xae, 0x34, 0x51, 0x54, + 0x34, 0x16, 0xac, 0x94, 0x5d, 0x65, 0xa5, 0xe4, 0x94, 0x5c, 0x9d, 0xf4, 0x6f, 0x27, 0x89, 0x33, 0x57, 0xc7, 0x25, + 0x89, 0xdb, 0xf6, 0x5b, 0xae, 0x23, 0xf3, 0x00, 0xe0, 0xd6, 0x76, 0x57, 0x7e, 0xde, 0xd6, 0xed, 0x83, 0xa6, 0x35, + 0x1f, 0x4b, 0xcd, 0xee, 0x65, 0x78, 0x47, 0xb3, 0xcb, 0x8e, 0xeb, 0x80, 0x9f, 0xa6, 0xa9, 0x52, 0x26, 0x64, 0x99, + 0xd3, 0x71, 0x9d, 0xdb, 0x49, 0x92, 0xe6, 0xc4, 0x8d, 0x85, 0x98, 0x06, 0xea, 0xfb, 0xb7, 0x37, 0x07, 0x3e, 0xcf, + 0xc6, 0xfb, 0x9d, 0x56, 0xab, 0x05, 0x17, 0xc0, 0xba, 0xce, 0x9c, 0xd1, 0x9b, 0xa7, 0xfc, 0x96, 0xb8, 0x2d, 0xa7, + 0xe5, 0xb4, 0x3b, 0xc7, 0x4e, 0xbb, 0x73, 0xe8, 0x3f, 0x3a, 0x76, 0x7b, 0x9f, 0x39, 0xce, 0x49, 0x44, 0x47, 0x39, + 0xfc, 0x70, 0x9c, 0x13, 0xa9, 0x78, 0xa9, 0xdf, 0x8e, 0xe3, 0x0f, 0x93, 0xbc, 0xd9, 0x76, 0x16, 0xfa, 0xd1, 0x71, + 0xe0, 0x50, 0x69, 0xe0, 0x7c, 0x3e, 0xea, 0x8c, 0x0e, 0x47, 0x4f, 0xba, 0xba, 0xb8, 0xf8, 0xac, 0x56, 0x1d, 0xab, + 0xff, 0x3b, 0x56, 0xb3, 0x5c, 0x64, 0xfc, 0x23, 0xd5, 0x39, 0x89, 0x0e, 0x88, 0x9e, 0x8d, 0x4d, 0x3b, 0xeb, 0x23, + 0xb5, 0x8f, 0xaf, 0x87, 0xa3, 0x4e, 0x55, 0x5d, 0xc2, 0xb8, 0x5f, 0x02, 0x79, 0xb2, 0x6f, 0x40, 0x3f, 0xb1, 0xd1, + 0xd4, 0x6e, 0x6e, 0x42, 0x54, 0xdb, 0xd5, 0x73, 0x1c, 0x9b, 0xf9, 0x9d, 0xc0, 0x19, 0x06, 0xa3, 0xab, 0x4a, 0x08, + 0x5c, 0x27, 0x22, 0xee, 0xab, 0x76, 0xe7, 0x18, 0xb7, 0xdb, 0x8f, 0xfc, 0x47, 0xc7, 0xc3, 0x16, 0x3e, 0xf4, 0x0f, + 0x9b, 0x07, 0xfe, 0x23, 0x7c, 0xdc, 0x3c, 0xc6, 0xc7, 0x2f, 0x8e, 0x87, 0xcd, 0x43, 0xff, 0x10, 0xb7, 0x9a, 0xc7, + 0x50, 0xd8, 0x3c, 0x6e, 0x1e, 0xcf, 0x9b, 0x87, 0xc7, 0xc3, 0x96, 0x2c, 0xed, 0xf8, 0x47, 0x47, 0xcd, 0x76, 0xcb, + 0x3f, 0x3a, 0xc2, 0x47, 0xfe, 0xa3, 0x47, 0xcd, 0xf6, 0x81, 0xff, 0xe8, 0xd1, 0xcb, 0xa3, 0x63, 0xff, 0x00, 0xde, + 0x1d, 0x1c, 0x0c, 0x0f, 0xfc, 0x76, 0xbb, 0x09, 0xff, 0xe0, 0x63, 0xbf, 0xa3, 0x7e, 0xb4, 0xdb, 0xfe, 0x41, 0x1b, + 0xb7, 0x92, 0xa3, 0x8e, 0xff, 0xe8, 0x09, 0x96, 0xff, 0xca, 0x6a, 0x58, 0xfe, 0x03, 0xdd, 0xe0, 0x27, 0x7e, 0xe7, + 0x91, 0xfa, 0x25, 0x3b, 0x9c, 0x1f, 0x1e, 0xff, 0xe0, 0xee, 0x6f, 0x9d, 0x43, 0x5b, 0xcd, 0xe1, 0xf8, 0xc8, 0x3f, + 0x38, 0xc0, 0x87, 0x6d, 0xff, 0xf8, 0x20, 0x6e, 0x1e, 0x76, 0xfc, 0x47, 0x8f, 0x87, 0xcd, 0xb6, 0xff, 0xf8, 0x31, + 0x6e, 0x35, 0x0f, 0xfc, 0x0e, 0x6e, 0xfb, 0x87, 0x07, 0xf2, 0xc7, 0x81, 0xdf, 0x99, 0x3f, 0x7e, 0xe2, 0x3f, 0x3a, + 0x8a, 0x1f, 0xf9, 0x87, 0xdf, 0x1e, 0x1e, 0xfb, 0x9d, 0x83, 0xf8, 0xe0, 0x91, 0xdf, 0x79, 0x3c, 0x7f, 0xe4, 0x1f, + 0xc6, 0xcd, 0xce, 0xa3, 0x7b, 0x5b, 0xb6, 0x3b, 0x3e, 0xe0, 0x48, 0xbe, 0x86, 0x17, 0x58, 0xbf, 0x80, 0xbf, 0xb1, + 0x6c, 0xfb, 0xef, 0xd8, 0x4d, 0xbe, 0xde, 0xf4, 0x89, 0x7f, 0xfc, 0x78, 0xa8, 0xaa, 0x43, 0x41, 0xd3, 0xd4, 0x80, + 0x26, 0xf3, 0xa6, 0x1a, 0x56, 0x76, 0xd7, 0x34, 0x1d, 0x99, 0xbf, 0x7a, 0xb0, 0x79, 0x13, 0x06, 0x56, 0xe3, 0xfe, + 0x87, 0xf6, 0x53, 0x2e, 0xf9, 0xc9, 0xfe, 0x58, 0x91, 0xfe, 0xb8, 0xf7, 0x99, 0xba, 0xdd, 0xf9, 0xb3, 0x2b, 0x9c, + 0x6e, 0x73, 0x7c, 0x64, 0x9f, 0x76, 0x7c, 0x70, 0xfa, 0x10, 0xcf, 0x47, 0xf6, 0x87, 0x7b, 0x3e, 0x52, 0xba, 0xe2, + 0x38, 0xbf, 0x16, 0x6b, 0x0e, 0x8e, 0x55, 0xab, 0xf8, 0xa9, 0xf0, 0x06, 0x39, 0x7c, 0x47, 0xac, 0xe8, 0x5e, 0x0b, + 0xc2, 0xa9, 0xed, 0x07, 0xe2, 0xc0, 0x62, 0xaf, 0x85, 0xe2, 0xb1, 0xc9, 0x36, 0x84, 0x84, 0x9f, 0x46, 0xc8, 0xb7, + 0x0f, 0xc1, 0x47, 0xf8, 0x87, 0xe3, 0x23, 0xb1, 0xf1, 0x51, 0xf3, 0xe5, 0x4b, 0x4f, 0x83, 0xf4, 0x14, 0x9c, 0xcb, + 0x67, 0x0f, 0x0e, 0x51, 0x35, 0xdc, 0x7d, 0x0a, 0x45, 0xb9, 0xab, 0x22, 0x5f, 0xef, 0x7e, 0x4d, 0xd8, 0x41, 0x9d, + 0x98, 0x24, 0xae, 0x76, 0xcb, 0x4c, 0xa5, 0xd4, 0xd1, 0x0f, 0xa5, 0x50, 0xea, 0xf8, 0x2d, 0xbf, 0x55, 0xba, 0x74, + 0xe0, 0x94, 0x2c, 0x59, 0x70, 0x11, 0xc2, 0x17, 0x6b, 0x13, 0x3e, 0x96, 0xdf, 0xb6, 0x85, 0xaf, 0x09, 0x40, 0xd2, + 0xcf, 0x50, 0x7d, 0xc8, 0x21, 0x70, 0x5d, 0x7d, 0xb7, 0x06, 0x9c, 0xc2, 0xfc, 0x06, 0x4e, 0xaa, 0x9a, 0xa8, 0xc4, + 0x04, 0xbc, 0x1d, 0xaf, 0x68, 0xc4, 0x42, 0xcf, 0xf5, 0xa6, 0x19, 0x1d, 0xd1, 0x2c, 0x6f, 0xd6, 0x8e, 0x6f, 0xca, + 0x93, 0x9b, 0xc8, 0x35, 0x9f, 0x46, 0xcd, 0xe0, 0x76, 0x6c, 0x32, 0xd0, 0xfe, 0x46, 0x57, 0x1b, 0x60, 0x6e, 0x81, + 0x4d, 0x49, 0x06, 0xb2, 0xb6, 0x52, 0xda, 0x5c, 0xa5, 0xb5, 0xb5, 0xfd, 0xce, 0x11, 0x72, 0x64, 0x31, 0xdc, 0x3b, + 0xfc, 0xbd, 0xd7, 0x3c, 0x68, 0xfd, 0x09, 0x59, 0xcd, 0xca, 0x8e, 0x2e, 0xb4, 0xbb, 0x2d, 0xad, 0xbe, 0x29, 0x5d, + 0x3f, 0x5b, 0xeb, 0x2a, 0x8a, 0xf8, 0x5c, 0xcd, 0xdd, 0x45, 0xdd, 0x54, 0x47, 0xb8, 0xd5, 0x0d, 0x11, 0x23, 0x36, + 0xf6, 0xec, 0x2f, 0x06, 0xab, 0x7b, 0x8d, 0xe5, 0x87, 0xc6, 0x51, 0x51, 0x55, 0x49, 0xd1, 0x42, 0xc6, 0x5b, 0x58, + 0xea, 0xa4, 0xcb, 0xa5, 0x97, 0x82, 0x8b, 0x9c, 0x58, 0x38, 0x85, 0x67, 0x54, 0x43, 0x72, 0x8a, 0x4b, 0x80, 0x24, + 0x82, 0x49, 0xaa, 0xfe, 0xaf, 0x8a, 0xcd, 0x0f, 0xed, 0xf8, 0xf2, 0x93, 0x30, 0x1d, 0x03, 0x15, 0x86, 0xe9, 0x78, + 0xcd, 0xad, 0xa6, 0x42, 0x46, 0x2b, 0xa5, 0x55, 0x57, 0x95, 0xfb, 0x2c, 0x7f, 0x7a, 0xf7, 0x5e, 0x5f, 0x80, 0xe6, + 0x82, 0x77, 0x5a, 0x46, 0x38, 0xaa, 0xcb, 0x9a, 0x1b, 0xe4, 0x8b, 0x93, 0x09, 0x15, 0xa1, 0xca, 0xd7, 0x04, 0x7d, + 0x02, 0x4e, 0xcd, 0x3a, 0xda, 0x1a, 0x25, 0xae, 0x94, 0xee, 0x24, 0xa2, 0x73, 0x36, 0xd4, 0xa2, 0x1e, 0x3b, 0xfa, + 0xe6, 0x80, 0xa6, 0x5c, 0x1a, 0xd2, 0xc6, 0xca, 0x1f, 0x33, 0x0c, 0x65, 0x46, 0x3e, 0x49, 0xb9, 0xdb, 0xfb, 0xa2, + 0xfc, 0xfa, 0xe9, 0xb6, 0x45, 0x48, 0x58, 0xfa, 0x71, 0x90, 0xd1, 0xe4, 0x9f, 0xc8, 0x17, 0x6c, 0xc8, 0xd3, 0x2f, + 0x2e, 0xe0, 0xab, 0xf4, 0x7e, 0x9c, 0xd1, 0x11, 0xf9, 0x02, 0x64, 0x7c, 0x20, 0xad, 0x0f, 0x60, 0x84, 0x8d, 0xdb, + 0x49, 0x82, 0xa5, 0xc6, 0xf4, 0x00, 0x85, 0x48, 0x81, 0xeb, 0x76, 0x8e, 0x5c, 0x47, 0xd9, 0xc4, 0xf2, 0x77, 0x4f, + 0x89, 0x53, 0xa9, 0x04, 0x38, 0xed, 0x8e, 0x7f, 0x14, 0x77, 0xfc, 0x27, 0xf3, 0xc7, 0xfe, 0x71, 0xdc, 0x7e, 0x3c, + 0x6f, 0xc2, 0xff, 0x1d, 0xff, 0x49, 0xd2, 0xec, 0xf8, 0x4f, 0xe0, 0xef, 0xb7, 0x87, 0xfe, 0x51, 0xdc, 0x6c, 0xfb, + 0xc7, 0xf3, 0x03, 0xff, 0xe0, 0x65, 0xbb, 0xe3, 0x1f, 0x38, 0x6d, 0x47, 0xb5, 0x03, 0x76, 0xad, 0xb8, 0xf3, 0x17, + 0x2b, 0x1b, 0x62, 0x43, 0x38, 0x4e, 0xe5, 0x9c, 0xba, 0xd8, 0x2b, 0xbf, 0xb1, 0xa8, 0xf7, 0xa7, 0x76, 0xd6, 0x3d, + 0x0b, 0x33, 0xf8, 0xd0, 0x4d, 0x7d, 0xef, 0xd6, 0xde, 0xe1, 0x1a, 0xbf, 0xd8, 0x30, 0x04, 0xec, 0x70, 0x17, 0xdb, + 0x47, 0xef, 0xe1, 0xdc, 0xba, 0xbc, 0x17, 0xdc, 0x5c, 0x8f, 0xb8, 0x9d, 0xb4, 0x55, 0x45, 0x73, 0x05, 0xa3, 0x64, + 0x16, 0x4c, 0x7e, 0x81, 0x41, 0x0e, 0xf2, 0x55, 0x54, 0xac, 0x8e, 0x0f, 0xa9, 0xaf, 0x19, 0xb7, 0x6e, 0x1f, 0xa0, + 0xd5, 0x81, 0x8d, 0x88, 0xc1, 0x7d, 0x11, 0x45, 0x61, 0x40, 0xaf, 0xb9, 0x69, 0x2b, 0x2c, 0x49, 0x7e, 0x41, 0xf3, + 0xbe, 0x0b, 0x45, 0x6e, 0xe0, 0x4a, 0x17, 0x9f, 0x5b, 0x7e, 0xec, 0xa7, 0x24, 0xec, 0xaa, 0x00, 0xcb, 0x43, 0x57, + 0xb0, 0x6b, 0x01, 0x3f, 0x2e, 0xda, 0xdb, 0xdb, 0xba, 0x5f, 0xa4, 0x02, 0x09, 0x73, 0xad, 0xbe, 0x11, 0x62, 0xb3, + 0x22, 0xd7, 0x46, 0x74, 0xd9, 0xaf, 0x44, 0x21, 0xd2, 0x78, 0xba, 0xa6, 0xa1, 0xf0, 0xc3, 0x54, 0x25, 0xd1, 0x58, + 0x0c, 0x0b, 0xb7, 0xe9, 0x01, 0x2a, 0xb8, 0x08, 0xad, 0xef, 0x00, 0xeb, 0x7d, 0xce, 0x45, 0x68, 0xce, 0xd2, 0x5a, + 0xd7, 0x06, 0x81, 0xa3, 0x37, 0xee, 0xf4, 0xde, 0xbc, 0x3f, 0x75, 0xd4, 0xf6, 0x3c, 0xd9, 0x8f, 0x3b, 0xbd, 0x13, + 0xe9, 0x33, 0x51, 0x27, 0xf1, 0x88, 0x3a, 0x89, 0xe7, 0xe8, 0x53, 0x99, 0x10, 0x49, 0x2b, 0xf6, 0xd5, 0xb4, 0xa5, + 0xcd, 0xa0, 0xbc, 0xbd, 0x93, 0x59, 0x22, 0x18, 0xdc, 0x71, 0xbd, 0x2f, 0x8f, 0xe1, 0xc1, 0x82, 0x95, 0x79, 0xd8, + 0x5a, 0x3b, 0xbc, 0x16, 0xa9, 0xf1, 0x0d, 0x8f, 0x58, 0x42, 0x4d, 0xe6, 0xb5, 0xee, 0xaa, 0x3c, 0x29, 0xb0, 0x5e, + 0x3b, 0x9f, 0x5d, 0x4f, 0x98, 0x70, 0xcd, 0x79, 0x86, 0x0f, 0xba, 0xc1, 0x89, 0x1c, 0xaa, 0x77, 0x55, 0x68, 0xe7, + 0xb5, 0xf9, 0x9a, 0x4f, 0x7d, 0x49, 0xf5, 0xec, 0xb5, 0x84, 0x80, 0x13, 0x72, 0xf1, 0x41, 0xaf, 0x74, 0x17, 0xdb, + 0xef, 0x8a, 0x93, 0xfd, 0xf8, 0xa0, 0x77, 0x15, 0x4c, 0x75, 0x7f, 0x2f, 0xf9, 0x78, 0x73, 0x5f, 0x09, 0x1f, 0xf7, + 0xe5, 0x51, 0x10, 0x75, 0x48, 0xd9, 0x28, 0xbf, 0x3c, 0x71, 0x7b, 0x27, 0x5a, 0x19, 0x70, 0x64, 0x60, 0xdd, 0x3d, + 0x6a, 0x99, 0xd3, 0x25, 0x09, 0x1f, 0xc3, 0x86, 0x54, 0x4d, 0xac, 0x41, 0x6a, 0x1e, 0xf7, 0xb8, 0xdd, 0x3b, 0x09, + 0x1d, 0xc9, 0x5b, 0x24, 0xf3, 0xc8, 0x83, 0x7d, 0x68, 0x1c, 0xf3, 0x09, 0xf5, 0x19, 0xdf, 0xbf, 0xa1, 0xd7, 0xcd, + 0x70, 0xca, 0x2a, 0xf7, 0x36, 0x28, 0x1d, 0xe5, 0x90, 0xdc, 0x78, 0xc4, 0xf5, 0xd9, 0xab, 0x4e, 0xe5, 0x6e, 0x3b, + 0x04, 0x9b, 0xc7, 0xb8, 0xe6, 0xa4, 0x4f, 0xce, 0x02, 0x8b, 0xf7, 0x4e, 0xf6, 0xc3, 0x15, 0x8c, 0x48, 0x7e, 0x5f, + 0x68, 0x47, 0x3b, 0x18, 0x36, 0x40, 0x6f, 0xae, 0xa3, 0xc4, 0x81, 0x71, 0xc8, 0x6b, 0x41, 0x5d, 0xb8, 0xbd, 0x7f, + 0xfd, 0x1f, 0xff, 0x4b, 0xfb, 0xd8, 0x4f, 0xf6, 0xe3, 0xb6, 0xe9, 0x6b, 0x65, 0x55, 0x8a, 0x13, 0x38, 0xee, 0x59, + 0x05, 0x85, 0xe9, 0x6d, 0x73, 0x9c, 0xb1, 0xa8, 0x19, 0x87, 0xc9, 0xc8, 0xed, 0x6d, 0xc7, 0xa6, 0x7d, 0x6c, 0x4b, + 0x43, 0x5d, 0x2f, 0x02, 0x7a, 0xfd, 0x4d, 0x07, 0x8f, 0xcc, 0xf9, 0x15, 0xb9, 0xb5, 0xed, 0x63, 0x48, 0xd5, 0xee, + 0xab, 0x1d, 0x45, 0x4a, 0xf5, 0x27, 0xc2, 0x34, 0x07, 0x4c, 0x6b, 0x27, 0x90, 0x0a, 0xd7, 0x29, 0x83, 0x5a, 0xff, + 0xf7, 0x7f, 0xfe, 0x97, 0xff, 0x66, 0x1e, 0x21, 0x56, 0xf5, 0xaf, 0xff, 0xfd, 0x3f, 0xff, 0x9f, 0xff, 0xfd, 0x5f, + 0xe1, 0xd4, 0x8a, 0x8e, 0x67, 0x49, 0xa6, 0xe2, 0x54, 0xc1, 0x2c, 0xc5, 0x5d, 0x1c, 0x48, 0xec, 0x9c, 0xb0, 0x5c, + 0xb0, 0x61, 0xfd, 0x4c, 0xd2, 0xb9, 0x1c, 0x50, 0xee, 0x4c, 0x0d, 0x9d, 0xdc, 0xe1, 0x45, 0x45, 0x50, 0x35, 0x94, + 0x4b, 0xc2, 0x2d, 0x4e, 0xf6, 0x01, 0xdf, 0x0f, 0x3b, 0xc6, 0xe9, 0x97, 0xcb, 0xb1, 0x30, 0x64, 0x02, 0x25, 0x45, + 0x55, 0xee, 0x40, 0x6c, 0x65, 0x01, 0x8f, 0x41, 0xc7, 0x2a, 0x96, 0xab, 0x57, 0x6b, 0xd3, 0xfd, 0x69, 0x96, 0x0b, + 0x36, 0x02, 0x94, 0x2b, 0x3f, 0xb1, 0x0c, 0x63, 0x37, 0x41, 0x57, 0x4c, 0xee, 0x0a, 0xd9, 0x8b, 0x22, 0xd0, 0xc3, + 0xe3, 0x3f, 0x15, 0x7f, 0x99, 0x80, 0x46, 0xe6, 0x78, 0x93, 0xf0, 0x56, 0x9b, 0xe7, 0x8f, 0x5a, 0xad, 0xe9, 0x2d, + 0x5a, 0x54, 0x23, 0xe0, 0x6d, 0x83, 0x49, 0x3a, 0xb6, 0x3b, 0x94, 0xf1, 0xef, 0xd2, 0x8d, 0xdd, 0x72, 0xc0, 0x17, + 0xee, 0xb4, 0x8a, 0xe2, 0xcf, 0x0b, 0xe9, 0x49, 0x65, 0xbf, 0x40, 0x9c, 0x5a, 0x3b, 0x9d, 0xaf, 0xb9, 0x3d, 0xb9, + 0x85, 0xd5, 0xaa, 0xa3, 0x5a, 0xc5, 0xed, 0xf5, 0xd3, 0x89, 0x76, 0x9c, 0xdd, 0x8e, 0x90, 0x1f, 0x42, 0xcc, 0x3b, + 0x6e, 0xe3, 0xb8, 0xb3, 0x28, 0xbb, 0x17, 0x82, 0x4f, 0xec, 0xc0, 0x3a, 0x0d, 0xe9, 0x90, 0x8e, 0x8c, 0xb3, 0x5e, + 0xbf, 0x57, 0x41, 0xf3, 0x22, 0x3e, 0xd8, 0x30, 0x96, 0x06, 0x49, 0x06, 0xd4, 0x9d, 0x56, 0xf1, 0x39, 0xec, 0xc0, + 0xc5, 0x28, 0xe1, 0xa1, 0x08, 0x24, 0xc1, 0x76, 0xed, 0xf0, 0x7c, 0x08, 0x3c, 0x89, 0x2f, 0x2c, 0x78, 0xba, 0xaa, + 0x2a, 0xb8, 0xcd, 0xeb, 0x67, 0x48, 0x0b, 0x5f, 0x36, 0xb7, 0xbb, 0x52, 0x5e, 0xb7, 0x6f, 0x75, 0xd4, 0xfb, 0x5d, + 0xcd, 0x5d, 0xa5, 0x05, 0x52, 0x07, 0x6d, 0x7e, 0xaf, 0xe4, 0xba, 0x7a, 0xfb, 0xb5, 0xf0, 0x5c, 0x09, 0xa6, 0xbb, + 0x5a, 0x4b, 0x16, 0x42, 0xad, 0x77, 0xe4, 0xdb, 0xd2, 0x64, 0x0a, 0xa7, 0x53, 0x59, 0x11, 0x75, 0x4f, 0xf6, 0x95, + 0xa6, 0x0b, 0xdc, 0x43, 0xa6, 0x74, 0xa8, 0x0c, 0x0a, 0x5d, 0x49, 0x6f, 0x05, 0xf5, 0x4b, 0xe7, 0x56, 0xc0, 0xa7, + 0xe3, 0x7a, 0xff, 0x0f, 0x82, 0x7a, 0x0b, 0xa7, 0xcf, 0x89, 0x00, 0x00}; } // namespace web_server } // namespace esphome diff --git a/esphome/components/web_server/server_index_v3.h b/esphome/components/web_server/server_index_v3.h index 98a4f255f5..39518197a3 100644 --- a/esphome/components/web_server/server_index_v3.h +++ b/esphome/components/web_server/server_index_v3.h @@ -3636,415 +3636,417 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0x9d, 0x85, 0xdf, 0xf1, 0x24, 0xec, 0x6a, 0x98, 0x92, 0x35, 0x98, 0x2e, 0x20, 0x16, 0xef, 0xfe, 0x43, 0xc1, 0x7e, 0x86, 0xbf, 0xb3, 0x14, 0xfe, 0x56, 0xb5, 0x77, 0x30, 0xbc, 0x7b, 0x7b, 0xf6, 0x01, 0x14, 0x1c, 0x31, 0x6a, 0x24, 0x37, 0x81, 0x36, 0x66, 0x18, 0x82, 0xca, 0x20, 0x88, 0x82, 0x78, 0x05, 0x27, 0x3b, 0xb2, 0x8e, 0x87, 0x77, 0xc3, - 0xdb, 0xdb, 0xdb, 0xff, 0xd3, 0xdc, 0xd3, 0x6e, 0xb7, 0x6d, 0x23, 0xfb, 0xbf, 0x4f, 0xc1, 0x30, 0xd9, 0x94, 0x4c, - 0x48, 0x9a, 0x94, 0x2c, 0x5b, 0x91, 0x2c, 0xb9, 0xcd, 0x47, 0xb7, 0xee, 0xba, 0x4d, 0x4f, 0xe2, 0xf6, 0xee, 0xae, - 0xeb, 0x63, 0x51, 0x12, 0x24, 0x71, 0x43, 0x91, 0x3a, 0x24, 0xe5, 0x8f, 0x2a, 0xdc, 0x67, 0xd9, 0x47, 0xb8, 0xcf, - 0xd0, 0x27, 0xbb, 0x67, 0x66, 0x00, 0x12, 0xfc, 0x92, 0xe4, 0x4d, 0xda, 0xde, 0xd3, 0x26, 0x11, 0x41, 0x00, 0x04, - 0x06, 0xc0, 0x60, 0xbe, 0xc7, 0x04, 0xd3, 0x46, 0x73, 0x1d, 0xf9, 0x2c, 0x98, 0x84, 0x90, 0x98, 0x24, 0x15, 0x08, - 0x9f, 0x95, 0x10, 0x3e, 0xc4, 0x45, 0xe5, 0x89, 0x35, 0xde, 0x2f, 0xc2, 0xdb, 0xaf, 0x7d, 0x5f, 0xe6, 0xdf, 0x05, - 0xd1, 0xc7, 0x59, 0xda, 0x02, 0x02, 0xd1, 0x40, 0x0d, 0x61, 0x79, 0xf1, 0x35, 0x57, 0x1c, 0x4f, 0xaf, 0xc7, 0xf7, - 0xd7, 0x5c, 0x38, 0x9d, 0x05, 0xa6, 0x7d, 0x35, 0x3a, 0x99, 0x7a, 0x37, 0x0a, 0x52, 0xa6, 0x03, 0x15, 0xbc, 0x7a, - 0x7c, 0x36, 0x5e, 0x27, 0x49, 0x18, 0x98, 0x51, 0x78, 0xab, 0x0e, 0x4f, 0xe8, 0x41, 0x54, 0x70, 0xe9, 0x51, 0x55, - 0xbe, 0x9a, 0xf8, 0xde, 0xe4, 0xc3, 0x40, 0x7d, 0xb2, 0xf1, 0x06, 0xc3, 0x12, 0xfd, 0x69, 0xa7, 0xea, 0x10, 0xc6, - 0xaa, 0x7c, 0xed, 0xfb, 0x27, 0x07, 0xd4, 0x62, 0x78, 0x72, 0x30, 0xf5, 0x6e, 0x86, 0x52, 0x8e, 0x10, 0xae, 0x40, - 0x1b, 0xf0, 0x58, 0x8c, 0x99, 0xc9, 0x51, 0x8c, 0xce, 0xfd, 0x13, 0xa6, 0xe5, 0x5c, 0x10, 0x04, 0x1d, 0xa1, 0xf1, - 0x6a, 0x13, 0x94, 0xab, 0xfa, 0x40, 0xf3, 0x7f, 0xfc, 0xa8, 0x65, 0x06, 0x89, 0x0b, 0x29, 0x5a, 0x17, 0xea, 0x7b, - 0xb0, 0x8a, 0x81, 0x21, 0x47, 0x74, 0x4d, 0xc4, 0x14, 0xf3, 0x75, 0x63, 0x92, 0x1a, 0x98, 0x6a, 0xc5, 0x5d, 0x01, - 0x8d, 0xc0, 0x7f, 0x4a, 0xac, 0xd1, 0x04, 0xd2, 0x2b, 0x4b, 0x08, 0x5e, 0x97, 0x84, 0xef, 0x74, 0x36, 0x79, 0xc0, - 0x38, 0x90, 0x9a, 0xe3, 0x77, 0x48, 0x20, 0xae, 0xf9, 0x3a, 0xa4, 0xf7, 0xca, 0xa2, 0xb4, 0xb8, 0xa9, 0x48, 0xa8, - 0x25, 0xe0, 0x72, 0x5a, 0x58, 0xa1, 0x5e, 0x79, 0xbd, 0x44, 0xf8, 0xc0, 0x47, 0x71, 0xd3, 0x92, 0x81, 0x32, 0x47, - 0x4b, 0x8c, 0xd2, 0x7d, 0x0c, 0xee, 0x5d, 0x92, 0x06, 0x81, 0x19, 0xda, 0x65, 0x6c, 0x84, 0x57, 0xf9, 0x1d, 0x16, - 0x13, 0xfa, 0xec, 0x85, 0x69, 0x1e, 0xc9, 0x97, 0x56, 0x7d, 0xf8, 0x64, 0x13, 0xe0, 0xa5, 0x17, 0x0f, 0x86, 0xc5, - 0x7d, 0x90, 0xb8, 0x63, 0x93, 0x36, 0xb3, 0xaa, 0x7c, 0x35, 0x1d, 0xfb, 0xd9, 0x62, 0xd3, 0xd1, 0x58, 0xb8, 0xc1, - 0xd4, 0x67, 0x17, 0xee, 0xf8, 0x5b, 0xac, 0xf3, 0x7a, 0xec, 0xbf, 0x82, 0x0a, 0xa9, 0x3a, 0x7c, 0xb2, 0xa1, 0x6b, - 0xbd, 0x0e, 0x8d, 0xa7, 0xb4, 0x05, 0xca, 0xdf, 0xe1, 0xb9, 0x77, 0x58, 0x44, 0xad, 0x71, 0xb0, 0x74, 0x15, 0x13, - 0x9e, 0x2d, 0x8e, 0x8c, 0xe7, 0x7e, 0x81, 0xbd, 0xa9, 0xf0, 0x43, 0x09, 0xe3, 0x0a, 0xc5, 0x01, 0x95, 0x77, 0xa6, - 0x3c, 0x58, 0xba, 0x72, 0xdf, 0x85, 0xb7, 0x62, 0xa4, 0x1c, 0x00, 0x14, 0xab, 0xf0, 0xf4, 0xd5, 0xe8, 0x44, 0xd6, - 0x0f, 0xa0, 0x10, 0x95, 0xfa, 0x85, 0x5f, 0xa9, 0xaa, 0xe4, 0x99, 0x80, 0x56, 0x77, 0xea, 0xf0, 0xe4, 0x40, 0xae, - 0x3d, 0x1c, 0xf5, 0xce, 0xa5, 0xc9, 0x61, 0xaf, 0x00, 0x84, 0x62, 0x59, 0xe5, 0xd6, 0x81, 0xe4, 0x78, 0xf9, 0xbd, - 0x44, 0xdb, 0x43, 0xa0, 0xc5, 0x50, 0xef, 0x65, 0x6b, 0x44, 0x36, 0x78, 0xa2, 0xb7, 0x11, 0xff, 0x37, 0x9f, 0x33, - 0xca, 0x34, 0x59, 0x10, 0x87, 0x91, 0x0a, 0xf3, 0x28, 0x67, 0xc8, 0x51, 0xa4, 0xcc, 0x5c, 0x38, 0xa3, 0xda, 0xdb, - 0x14, 0x20, 0x72, 0x50, 0x6e, 0x2a, 0x4d, 0x6c, 0xa4, 0xe7, 0x3f, 0x14, 0x3e, 0x99, 0x12, 0x56, 0xca, 0x06, 0xd8, - 0x9c, 0x79, 0xe8, 0xf2, 0xad, 0x67, 0xfc, 0x4f, 0x68, 0xcc, 0x5d, 0x63, 0xe9, 0x1a, 0xef, 0x83, 0xab, 0xb4, 0x76, - 0x75, 0xb2, 0xac, 0x61, 0x06, 0xeb, 0x6b, 0x10, 0x6b, 0x87, 0x4b, 0x40, 0xb8, 0x5c, 0xc0, 0xb3, 0xb8, 0x75, 0xc0, - 0x85, 0x1b, 0xcd, 0x99, 0x48, 0xd6, 0x25, 0xde, 0x26, 0x1c, 0x2a, 0xba, 0x04, 0x16, 0x08, 0x44, 0x25, 0x18, 0x1c, - 0xcf, 0x9a, 0x24, 0x91, 0xff, 0x37, 0x76, 0x0f, 0x9c, 0x67, 0x9c, 0x84, 0x2b, 0x90, 0x4e, 0xb8, 0x73, 0x2e, 0x6d, - 0x36, 0x80, 0x96, 0xd9, 0xe7, 0x73, 0x1f, 0x3f, 0x32, 0x29, 0x7f, 0x54, 0x12, 0xce, 0xe7, 0x3e, 0xd3, 0xa4, 0x3c, - 0x53, 0xd9, 0x67, 0x4e, 0x1f, 0xd9, 0x22, 0x46, 0xb1, 0x9e, 0x36, 0x9d, 0x9c, 0x9c, 0x14, 0x14, 0x7a, 0x5d, 0x62, - 0xd6, 0x51, 0x1b, 0x75, 0x83, 0x0a, 0x5d, 0xbe, 0x2e, 0xf9, 0xc9, 0x34, 0xa7, 0xe1, 0x7a, 0xec, 0x33, 0x13, 0xb7, - 0x3b, 0x7c, 0x72, 0x33, 0x5e, 0x8f, 0xc7, 0x3e, 0x25, 0x86, 0x82, 0x48, 0x5b, 0x61, 0x8c, 0x12, 0xb0, 0x54, 0xef, - 0x23, 0x81, 0x96, 0x94, 0x87, 0x0f, 0xd6, 0x71, 0xc0, 0x36, 0xd0, 0x07, 0x12, 0x90, 0x76, 0x55, 0x0f, 0xed, 0x40, - 0x05, 0x76, 0x85, 0xc5, 0x6a, 0xbf, 0x86, 0x92, 0x1b, 0x5c, 0xaa, 0xef, 0x11, 0xc2, 0x98, 0xbd, 0xfe, 0x15, 0xed, - 0x5d, 0xd5, 0x50, 0xc9, 0xc8, 0x87, 0xe7, 0x11, 0x53, 0x0d, 0xf5, 0xb5, 0xe7, 0xce, 0x83, 0x30, 0x4e, 0xbc, 0x89, - 0x7a, 0xd5, 0x3f, 0xf3, 0xb4, 0xcb, 0x65, 0xa2, 0xe9, 0x57, 0xc6, 0x5f, 0xe5, 0x8c, 0x4f, 0x02, 0x15, 0x62, 0xc2, - 0xa7, 0x86, 0x3a, 0xf2, 0xe9, 0xd9, 0x56, 0x4f, 0xa0, 0x5c, 0xac, 0xf3, 0xd7, 0x01, 0xd4, 0x2a, 0xe5, 0x8e, 0xc2, - 0xa4, 0x80, 0x90, 0x3b, 0xea, 0xaf, 0x7a, 0x9f, 0xa4, 0x32, 0xaf, 0xd6, 0x1b, 0xa4, 0x15, 0x92, 0xfc, 0x76, 0xc5, - 0x70, 0xe7, 0xc2, 0x47, 0x90, 0x9e, 0x1f, 0xc9, 0xf6, 0xed, 0x85, 0x7b, 0x7a, 0xf4, 0x75, 0x91, 0xf0, 0x00, 0x02, - 0x01, 0x8c, 0xcb, 0x82, 0x30, 0x51, 0x20, 0x86, 0x17, 0x7c, 0x70, 0x54, 0xb6, 0x87, 0xe5, 0xbd, 0x6a, 0x7a, 0xca, - 0xb1, 0xc0, 0x4b, 0xbc, 0x2c, 0x45, 0xf6, 0x77, 0x0c, 0x47, 0x59, 0x88, 0xd8, 0xc3, 0xbd, 0xb0, 0x60, 0xf9, 0x0a, - 0x64, 0x9b, 0x84, 0xd8, 0x8b, 0x17, 0xf6, 0x93, 0x4d, 0x7c, 0x2a, 0x6e, 0xed, 0xb3, 0x18, 0xd7, 0x12, 0xe8, 0x11, - 0x7e, 0x8d, 0xa7, 0xaa, 0x72, 0x2a, 0xae, 0x1a, 0xac, 0x5b, 0xc0, 0x9f, 0x9a, 0xa0, 0x72, 0x45, 0x52, 0x77, 0x8d, - 0xa7, 0xa0, 0x16, 0x74, 0x57, 0xe9, 0xe8, 0x41, 0x58, 0x9e, 0x8c, 0xa4, 0x4a, 0xc0, 0xb6, 0x16, 0x6f, 0x04, 0xc0, - 0x5c, 0x9c, 0x08, 0x18, 0xa5, 0xd7, 0x40, 0x3f, 0x42, 0xac, 0x2a, 0x31, 0x47, 0x23, 0x94, 0xd3, 0x85, 0x79, 0xc1, - 0x6a, 0x9d, 0x60, 0x0c, 0x72, 0x18, 0x00, 0x4b, 0x55, 0x05, 0xb9, 0x45, 0x40, 0xe6, 0x39, 0x17, 0x94, 0xaa, 0x8a, - 0x37, 0xad, 0x96, 0x71, 0xd1, 0x0d, 0xe0, 0x38, 0x9c, 0x06, 0x4a, 0xf0, 0xe1, 0x31, 0xe2, 0xd3, 0x98, 0x18, 0x79, - 0x02, 0x0f, 0x6d, 0x82, 0x9a, 0xee, 0x1a, 0x04, 0x32, 0xa1, 0x7e, 0xfa, 0x9a, 0x5f, 0x3b, 0x59, 0x88, 0x4b, 0x5c, - 0x98, 0xe6, 0xe8, 0xc9, 0x26, 0x48, 0x4f, 0x01, 0x76, 0x83, 0x27, 0x1b, 0x37, 0x33, 0xa2, 0x52, 0x2f, 0x54, 0xb2, - 0xa0, 0x1a, 0x21, 0x18, 0x46, 0xe9, 0x75, 0xee, 0xd2, 0x98, 0xcf, 0x17, 0xb6, 0x24, 0x95, 0x2b, 0x68, 0xd3, 0x34, - 0xe0, 0x96, 0x4b, 0xab, 0xc8, 0x5b, 0xba, 0xd1, 0x3d, 0x19, 0x3a, 0x19, 0xb2, 0x35, 0x94, 0xae, 0x2a, 0x74, 0x1f, - 0x10, 0x00, 0xe8, 0x6a, 0x50, 0x95, 0xaf, 0xb2, 0x32, 0xc6, 0x67, 0x9b, 0x59, 0x7b, 0xc0, 0xb7, 0xae, 0xd5, 0xe7, - 0xcc, 0x22, 0x91, 0x06, 0x35, 0xe9, 0x6b, 0x71, 0xc3, 0xf4, 0xe2, 0xe2, 0xf4, 0x82, 0xe2, 0x46, 0xc3, 0xc9, 0xd0, - 0x4d, 0x41, 0xe3, 0xc6, 0x99, 0x61, 0xba, 0xc3, 0xfa, 0x15, 0xa5, 0x77, 0x7f, 0xe8, 0x72, 0x30, 0x58, 0x8e, 0x00, - 0x96, 0x83, 0xa8, 0xeb, 0x9f, 0xde, 0x9d, 0x65, 0xf9, 0x15, 0x41, 0x34, 0x3e, 0xe2, 0x1b, 0x33, 0x46, 0x32, 0x23, - 0x42, 0x12, 0x83, 0x32, 0x21, 0x2a, 0xd9, 0x16, 0x8a, 0xe0, 0x68, 0xd0, 0xd8, 0xe9, 0x68, 0x44, 0x83, 0x41, 0x88, - 0xad, 0xa2, 0xf4, 0xe4, 0x80, 0x6a, 0xd3, 0xa5, 0x48, 0x95, 0x00, 0x0c, 0x11, 0xcc, 0x30, 0x87, 0x02, 0xa4, 0x82, - 0x1e, 0x38, 0x39, 0x7f, 0x63, 0x2d, 0x51, 0x01, 0xe9, 0x9c, 0x16, 0x29, 0x1a, 0x6c, 0xa5, 0x0e, 0x4f, 0x30, 0xb9, - 0x23, 0x5c, 0xeb, 0x10, 0xfe, 0xe8, 0xe4, 0x80, 0x1e, 0x95, 0xd2, 0x89, 0xc8, 0x3b, 0x11, 0x02, 0xca, 0x1e, 0xef, - 0xe0, 0x41, 0x47, 0x25, 0x4e, 0xd8, 0x0a, 0x4a, 0xdd, 0x54, 0x55, 0x96, 0x9c, 0x82, 0xe2, 0x71, 0xd6, 0x20, 0x08, - 0x8b, 0x0d, 0xc6, 0xef, 0xaa, 0xb2, 0x74, 0xef, 0x70, 0xe6, 0xe2, 0x8d, 0x7b, 0xa7, 0x39, 0xfc, 0x55, 0x7e, 0xd6, - 0xe2, 0xe2, 0x59, 0x9b, 0xf0, 0xc5, 0x05, 0x0f, 0x2b, 0x41, 0x39, 0x6b, 0x0b, 0xb4, 0x5c, 0xa9, 0x59, 0xdc, 0x85, - 0x58, 0xdc, 0x69, 0xc3, 0xe2, 0x4e, 0xb7, 0x2c, 0xae, 0xcf, 0x17, 0x52, 0xc9, 0x40, 0x17, 0xa1, 0xd7, 0x6c, 0x06, - 0x3c, 0x4e, 0x8f, 0xf4, 0xf8, 0x39, 0x43, 0x38, 0x99, 0xb1, 0x0f, 0x56, 0xa3, 0x0d, 0xb0, 0xaa, 0x83, 0x8b, 0x04, - 0x88, 0xea, 0xc4, 0xb3, 0x53, 0x37, 0x91, 0x04, 0x02, 0x9a, 0x5f, 0x9e, 0x2f, 0xec, 0x52, 0x6c, 0x68, 0x68, 0x8b, - 0x86, 0x99, 0x2e, 0xb6, 0xcc, 0x74, 0x52, 0x38, 0xba, 0x7c, 0xda, 0x74, 0x08, 0xe5, 0x49, 0xc1, 0x1e, 0x04, 0x4b, - 0x7a, 0xdc, 0x32, 0xc5, 0x7d, 0xd8, 0x8c, 0x63, 0xa5, 0x1d, 0xb5, 0x72, 0xe3, 0xf8, 0x36, 0x8c, 0x40, 0x15, 0x0d, - 0xdd, 0x3c, 0x6c, 0x4b, 0x2d, 0xbd, 0x80, 0x47, 0xb9, 0x6a, 0xdc, 0x4c, 0xf9, 0x7b, 0x79, 0x4b, 0xb5, 0x3a, 0x1d, - 0xaa, 0xb1, 0x72, 0x93, 0x84, 0x45, 0x08, 0x74, 0x17, 0xd2, 0x21, 0xfc, 0x3f, 0xd9, 0x66, 0x35, 0x38, 0xc4, 0x97, - 0xb0, 0x3a, 0x62, 0xe8, 0x15, 0x90, 0x60, 0xa4, 0x7b, 0x0a, 0xf4, 0x8d, 0x14, 0x31, 0x33, 0xca, 0x00, 0xff, 0x03, - 0x1e, 0x57, 0x2d, 0x92, 0x7c, 0x3a, 0x9d, 0x23, 0xdd, 0x5a, 0xb9, 0xd3, 0xf7, 0x60, 0xf1, 0xa0, 0xb5, 0x0c, 0xf0, - 0x5e, 0x90, 0xe3, 0x63, 0x46, 0x44, 0x13, 0x4e, 0x72, 0x24, 0x89, 0x58, 0x92, 0xdb, 0x86, 0x82, 0x5b, 0xb9, 0x6b, - 0xce, 0xae, 0x36, 0xad, 0xf4, 0x60, 0xee, 0xe9, 0x15, 0xac, 0x09, 0xa8, 0xcd, 0x1f, 0x0c, 0x33, 0x59, 0x9b, 0x6f, - 0x38, 0x47, 0x3a, 0xa8, 0xc4, 0x2e, 0x21, 0xf1, 0xb5, 0x2d, 0xb8, 0xe5, 0x51, 0x04, 0xb7, 0xd6, 0xa5, 0x7d, 0x95, - 0x3e, 0x9d, 0xe3, 0x2f, 0xe7, 0x2a, 0x7d, 0x3a, 0xc6, 0x5f, 0xad, 0x2b, 0x4c, 0xe9, 0x59, 0x23, 0x26, 0x90, 0xe6, - 0xac, 0x0e, 0x0b, 0xfb, 0x89, 0x0c, 0x73, 0x1f, 0xb0, 0x6d, 0xf8, 0x02, 0x3f, 0x7e, 0xb2, 0x89, 0xc1, 0x15, 0x5d, - 0x9e, 0x43, 0x60, 0x45, 0x7a, 0x5a, 0x5b, 0x3e, 0x6f, 0x28, 0x1f, 0xeb, 0x7f, 0xf0, 0xc5, 0x8f, 0xbb, 0x24, 0xcc, - 0xef, 0x94, 0xa2, 0x90, 0xe3, 0x7a, 0xec, 0x05, 0x6e, 0x74, 0x7f, 0x4d, 0x5c, 0x88, 0x26, 0x49, 0x7b, 0x1f, 0xe5, - 0xdc, 0xff, 0x7d, 0xd1, 0x0e, 0x20, 0x91, 0x74, 0x59, 0xf7, 0xfc, 0xa2, 0x1f, 0xfc, 0x3d, 0x92, 0xe8, 0xbb, 0x02, - 0x9f, 0xca, 0x17, 0xa4, 0xf0, 0xa1, 0xeb, 0x27, 0x1b, 0x8d, 0x55, 0xbb, 0x29, 0xcd, 0xb6, 0x44, 0x40, 0xc2, 0xf2, - 0x20, 0xcf, 0xbb, 0x9c, 0x7a, 0x3d, 0x54, 0xf4, 0x8f, 0xc3, 0x3b, 0xf3, 0xc9, 0x26, 0x39, 0x55, 0x97, 0x6e, 0xf4, - 0x81, 0x4d, 0xcd, 0x89, 0x17, 0x4d, 0x7c, 0x20, 0x1e, 0xc7, 0xbe, 0x1b, 0x7c, 0xe0, 0x8f, 0x66, 0xb8, 0x4e, 0xd0, - 0x74, 0x67, 0x27, 0x8b, 0x2c, 0x60, 0x42, 0xf2, 0x43, 0xa4, 0x6a, 0x6b, 0xa0, 0xa0, 0xbc, 0xca, 0xe4, 0x6f, 0x39, - 0xa1, 0x98, 0xd7, 0x32, 0xc0, 0xf2, 0x1c, 0xac, 0x89, 0xc0, 0x95, 0xdf, 0x50, 0x71, 0xbd, 0x54, 0x43, 0x9e, 0x2a, - 0xa9, 0xdc, 0xb2, 0x5c, 0xb4, 0xd7, 0xd8, 0xc3, 0x7f, 0xff, 0x39, 0x28, 0x79, 0xc8, 0xe7, 0xb2, 0x5e, 0x3e, 0x6d, - 0x86, 0x50, 0x6a, 0x92, 0x0b, 0xd9, 0x03, 0x3e, 0xce, 0x09, 0xcc, 0xe6, 0x4f, 0xcb, 0x8d, 0xdd, 0x38, 0x5e, 0x2f, - 0xd9, 0x94, 0x54, 0x6b, 0xa7, 0xf9, 0xa0, 0x8a, 0x7c, 0x88, 0x3c, 0xb0, 0x5f, 0xd6, 0xad, 0xe3, 0xc3, 0x57, 0x60, - 0xca, 0x05, 0x04, 0x65, 0x38, 0x9b, 0xa9, 0xb9, 0x28, 0x60, 0x47, 0x33, 0xe7, 0xf0, 0x97, 0xf5, 0x37, 0x6f, 0xec, - 0x6f, 0xb2, 0xc6, 0x01, 0x10, 0xc6, 0xc2, 0x2e, 0x85, 0xd3, 0xc5, 0xd2, 0x78, 0xc5, 0x8c, 0x66, 0x6e, 0xd0, 0x3c, - 0x9d, 0xcb, 0xc2, 0x16, 0x5f, 0x31, 0x36, 0x05, 0x82, 0xdb, 0xa8, 0x94, 0x5e, 0xfb, 0xec, 0x86, 0x65, 0x36, 0x2f, - 0xd5, 0x8f, 0xd5, 0xb4, 0xc0, 0xa0, 0x9c, 0x5c, 0x93, 0xc9, 0xa9, 0x3a, 0x69, 0x4a, 0x23, 0x9c, 0x03, 0x9f, 0xb9, - 0x7c, 0xc4, 0x4a, 0x47, 0x6a, 0x64, 0xa8, 0xd2, 0x00, 0x1a, 0x47, 0x76, 0xda, 0x50, 0xde, 0x03, 0x44, 0xdd, 0x30, - 0x36, 0xc3, 0xd1, 0x7b, 0x90, 0xc4, 0x80, 0xc3, 0xc9, 0x87, 0x93, 0xa7, 0xe5, 0x52, 0x93, 0x26, 0x88, 0xd5, 0xc9, - 0xd2, 0x54, 0x12, 0xd2, 0x08, 0x33, 0x70, 0xf4, 0x87, 0x10, 0xe2, 0xaa, 0xda, 0xb5, 0x51, 0x8a, 0x33, 0x1f, 0x63, - 0x8a, 0xef, 0x80, 0xc5, 0x71, 0x23, 0xc0, 0xb2, 0x45, 0x37, 0xd4, 0xbc, 0x76, 0x11, 0x1e, 0x79, 0xb9, 0x61, 0x1b, - 0x40, 0x12, 0xe0, 0x04, 0xcb, 0xdf, 0xc2, 0xeb, 0xe5, 0x7a, 0xc9, 0x0d, 0xf9, 0xa2, 0xf9, 0x58, 0xe5, 0x46, 0x56, - 0x4d, 0xef, 0x6f, 0x55, 0x3e, 0xa8, 0xc2, 0x35, 0x5d, 0x3b, 0x34, 0xad, 0x80, 0x7a, 0x2b, 0x52, 0x25, 0xec, 0x40, - 0x8c, 0xa9, 0x84, 0x5f, 0xd9, 0x6c, 0xc6, 0x26, 0x49, 0xac, 0x0b, 0x19, 0x53, 0x16, 0x56, 0x1b, 0xb4, 0x77, 0x8f, - 0x06, 0xea, 0x0f, 0x10, 0x5c, 0x44, 0x44, 0x9f, 0xe3, 0x03, 0x12, 0x3c, 0x53, 0x3d, 0x98, 0xa8, 0xc7, 0x22, 0x88, - 0xf8, 0x57, 0x40, 0xcc, 0x5c, 0x53, 0x8e, 0x43, 0xe3, 0xf7, 0x4f, 0xbe, 0x2f, 0xc2, 0xcc, 0xdc, 0x6f, 0x3b, 0x2a, - 0xda, 0x76, 0x7c, 0x37, 0xce, 0x37, 0x1d, 0xc7, 0x4e, 0x55, 0x03, 0x9c, 0x5a, 0x3f, 0x94, 0xb6, 0x31, 0x5d, 0x50, - 0x03, 0xf5, 0xfc, 0xed, 0xab, 0xbf, 0xbd, 0x79, 0xbd, 0x2f, 0x46, 0xc0, 0x2e, 0xdb, 0xd0, 0xe5, 0x3a, 0xd8, 0xd2, - 0xe9, 0x4f, 0x3f, 0x3c, 0xac, 0xdb, 0x96, 0xf3, 0xc2, 0x51, 0x0d, 0xb2, 0x43, 0x96, 0xf0, 0xe2, 0x24, 0xbc, 0x61, - 0xd1, 0x27, 0x83, 0x41, 0xee, 0xbc, 0x7e, 0xb8, 0x6f, 0x7f, 0x7c, 0xf3, 0xc3, 0xde, 0x43, 0x3d, 0x72, 0x6c, 0xc0, - 0xed, 0x49, 0xb8, 0x7a, 0xc0, 0xec, 0xda, 0xaa, 0xa1, 0x4e, 0xfc, 0x30, 0x66, 0x0d, 0x23, 0x78, 0x75, 0xfe, 0xf6, - 0x3d, 0x82, 0x2b, 0x27, 0x41, 0xa8, 0xab, 0x4f, 0x9b, 0xfc, 0x8f, 0xef, 0xde, 0xbc, 0x7f, 0xaf, 0x1a, 0x98, 0x96, - 0x39, 0x96, 0x7b, 0xe7, 0x9b, 0x78, 0xc7, 0x8d, 0x53, 0xbb, 0xd7, 0xe9, 0x56, 0x23, 0x46, 0xba, 0x38, 0x1b, 0x2a, - 0xab, 0x6c, 0x73, 0x7e, 0xdb, 0xf1, 0x2f, 0x13, 0xf7, 0xbb, 0xd7, 0xbc, 0x6a, 0xf0, 0xd1, 0xf6, 0x2b, 0xb5, 0x50, - 0xb2, 0xf4, 0x82, 0xeb, 0x9a, 0x52, 0xf7, 0xae, 0xa6, 0x14, 0xd8, 0xc7, 0x0a, 0x7e, 0x5c, 0x87, 0x4b, 0x89, 0x1c, - 0x61, 0x77, 0xbb, 0xc1, 0x25, 0xf1, 0x70, 0x9f, 0x30, 0x68, 0x9e, 0x56, 0xa3, 0x3c, 0xea, 0x9a, 0x62, 0xce, 0x78, - 0x65, 0xb0, 0x9d, 0xf8, 0x60, 0x7d, 0xcd, 0x64, 0x3d, 0x63, 0x91, 0x54, 0xe5, 0xbe, 0x13, 0x83, 0x12, 0x57, 0x40, - 0xcd, 0x48, 0x37, 0xc3, 0xef, 0x94, 0x95, 0x3b, 0x05, 0x93, 0x66, 0x73, 0x1c, 0x26, 0x49, 0xb8, 0xec, 0x39, 0xf6, - 0xea, 0x4e, 0x55, 0xfa, 0x42, 0xd8, 0xc1, 0x2d, 0xae, 0x7b, 0xbf, 0xfd, 0xa7, 0x84, 0xe6, 0xa9, 0xfc, 0x3a, 0x61, - 0xcb, 0x15, 0x8b, 0xdc, 0x64, 0x1d, 0xb1, 0x54, 0xf9, 0xed, 0x7f, 0x5f, 0x95, 0x18, 0xfb, 0xbe, 0xdc, 0x86, 0x48, - 0x7a, 0xb9, 0xc9, 0xb5, 0x1f, 0xde, 0x3e, 0xca, 0x7d, 0xab, 0x76, 0x54, 0x5e, 0x78, 0xf3, 0x45, 0x56, 0xfb, 0x34, - 0xd9, 0x32, 0x37, 0x31, 0x7a, 0xd2, 0x07, 0x28, 0xe7, 0xe1, 0x6d, 0xef, 0xb7, 0xff, 0x64, 0x02, 0x9b, 0x9d, 0xbb, - 0xae, 0x7e, 0xa0, 0xc5, 0x15, 0xad, 0xaf, 0x53, 0x59, 0x62, 0x78, 0x5f, 0x59, 0xe0, 0x4a, 0x21, 0xed, 0xca, 0xaa, - 0xf2, 0x6d, 0xcb, 0x9c, 0xbe, 0xf5, 0xe6, 0x8b, 0x4f, 0x9d, 0x14, 0x00, 0x74, 0xe7, 0xac, 0xa0, 0xd2, 0x67, 0x98, - 0xd6, 0xa8, 0xb7, 0xff, 0x82, 0x7d, 0xe2, 0xbc, 0x76, 0x4d, 0xe9, 0x73, 0xcc, 0x86, 0x4b, 0x6e, 0x5f, 0x8d, 0x46, - 0x59, 0x5a, 0x52, 0xb9, 0x3d, 0x78, 0x87, 0x9d, 0x56, 0x4a, 0x38, 0x79, 0xd1, 0xb3, 0x75, 0x0a, 0xdb, 0xb2, 0x07, - 0x40, 0xd0, 0xce, 0xb9, 0x06, 0x1c, 0xcd, 0xf8, 0x9a, 0xdc, 0x95, 0x2a, 0xdf, 0xae, 0x20, 0x6b, 0x28, 0xc5, 0x94, - 0x96, 0x99, 0xd6, 0xd0, 0xa8, 0x1f, 0xce, 0x6d, 0xe4, 0xae, 0x48, 0x49, 0xa0, 0xa0, 0xc6, 0x04, 0x84, 0x2e, 0x25, - 0x2e, 0xfa, 0xc6, 0xf5, 0x6f, 0xf6, 0x63, 0xa8, 0x9a, 0x6f, 0x30, 0xbc, 0x9a, 0xff, 0xbc, 0xcb, 0x1b, 0xef, 0xe5, - 0xfd, 0xef, 0x6e, 0x4c, 0x15, 0xf6, 0xb6, 0xc9, 0xbc, 0xfa, 0xc7, 0xdd, 0xe6, 0xd5, 0x17, 0x7b, 0x99, 0x57, 0xff, - 0xf8, 0xd9, 0xcd, 0xab, 0xdf, 0xca, 0xe6, 0xd5, 0xb0, 0x89, 0xdf, 0xb0, 0xbd, 0x8c, 0x9e, 0x85, 0x91, 0x51, 0x78, - 0x1b, 0x0f, 0x1c, 0xce, 0xf4, 0xc4, 0x93, 0x05, 0x03, 0x29, 0x12, 0x07, 0x97, 0x1f, 0xce, 0xc1, 0x36, 0xb9, 0xd9, - 0xfa, 0xf8, 0x73, 0xd9, 0x1e, 0xfb, 0xe1, 0x5c, 0x95, 0x82, 0xa5, 0x07, 0x3c, 0x58, 0x7a, 0x1e, 0x00, 0x91, 0x0c, - 0xe6, 0x6c, 0x43, 0x84, 0x4b, 0x34, 0x0f, 0x75, 0x61, 0x78, 0xd7, 0x53, 0xf5, 0xcc, 0xf0, 0xbb, 0x25, 0x4c, 0xea, - 0x7a, 0x2a, 0x84, 0x4e, 0xca, 0x1a, 0xb6, 0x9e, 0x8b, 0xb0, 0x50, 0x72, 0x0f, 0x99, 0x83, 0x0d, 0x85, 0x58, 0xda, - 0xa8, 0xbf, 0xdc, 0x39, 0x2f, 0x2f, 0x9d, 0x7e, 0xdb, 0x81, 0xb8, 0x26, 0x20, 0x83, 0xc0, 0x02, 0xbb, 0xdf, 0x6e, - 0x43, 0xc1, 0xad, 0x54, 0xd0, 0x82, 0x02, 0x4f, 0x2a, 0xe8, 0x40, 0xc1, 0x44, 0x2a, 0x38, 0x82, 0x82, 0xa9, 0x54, - 0x70, 0x0c, 0x05, 0x37, 0x6a, 0x7a, 0x19, 0x64, 0xc3, 0x3d, 0xd6, 0xaf, 0x0c, 0x62, 0x3b, 0x45, 0xd9, 0xb1, 0xe1, - 0x80, 0x59, 0x9c, 0x3b, 0xef, 0x85, 0x06, 0xc9, 0x9f, 0x7b, 0x91, 0x71, 0xbb, 0x60, 0x94, 0x63, 0xe1, 0x35, 0x52, - 0x08, 0x56, 0x12, 0x82, 0xcb, 0x91, 0x88, 0x5d, 0x24, 0xe0, 0xa0, 0xa8, 0x3a, 0x88, 0x14, 0xfb, 0xd9, 0xca, 0x89, - 0xf8, 0x4f, 0xd2, 0x5a, 0xe6, 0xef, 0xe8, 0x73, 0x66, 0xb6, 0x05, 0x72, 0x8b, 0x27, 0x4d, 0x96, 0x5b, 0x7f, 0x0e, - 0xfb, 0x94, 0xd7, 0x6c, 0xbc, 0x9e, 0x2b, 0xe7, 0xe1, 0x7c, 0xa7, 0x2d, 0x8a, 0xfc, 0x0a, 0x46, 0xa9, 0x92, 0x82, - 0xce, 0x14, 0xdb, 0x92, 0x7f, 0x8b, 0x1e, 0xd3, 0x62, 0xfd, 0x04, 0xc6, 0xa6, 0x24, 0x84, 0x6a, 0xe1, 0x3b, 0x00, - 0x23, 0xc9, 0x18, 0xe4, 0x1c, 0xe0, 0x2c, 0x3d, 0x5f, 0xb8, 0xd2, 0x78, 0x86, 0xdf, 0xb3, 0x38, 0x76, 0xe7, 0xa2, - 0x7e, 0x75, 0x9c, 0xe0, 0x1b, 0x93, 0x71, 0xe8, 0x08, 0x40, 0x90, 0xf5, 0x7a, 0x15, 0x1b, 0x1e, 0x30, 0x41, 0x06, - 0x73, 0x35, 0xd8, 0x50, 0xb9, 0xc1, 0x8b, 0x67, 0xc1, 0x12, 0x16, 0x4d, 0x53, 0xe0, 0xf0, 0xdf, 0x30, 0xbf, 0x5c, - 0x98, 0xb8, 0xf3, 0x72, 0x11, 0xed, 0x83, 0x54, 0x1e, 0x5b, 0x66, 0x19, 0x52, 0x28, 0xfc, 0x14, 0x53, 0x07, 0x3f, - 0x9c, 0xff, 0xae, 0x76, 0x0e, 0x5b, 0xec, 0x53, 0xde, 0x07, 0x46, 0x90, 0x8c, 0x2c, 0x84, 0xb1, 0x62, 0x01, 0x08, - 0x7b, 0x41, 0xb2, 0x30, 0xd1, 0x2b, 0x5b, 0x6b, 0x05, 0xba, 0x61, 0xe1, 0xda, 0x6e, 0xca, 0xb1, 0x28, 0x7a, 0xd1, - 0x7c, 0xec, 0x6a, 0x4e, 0xeb, 0xd8, 0x10, 0x7f, 0x2c, 0xbb, 0xa3, 0xa7, 0xd8, 0x83, 0x32, 0xf5, 0x6e, 0x36, 0xb3, - 0x30, 0x48, 0xcc, 0x99, 0xbb, 0xf4, 0xfc, 0xfb, 0xde, 0x32, 0x0c, 0xc2, 0x78, 0xe5, 0x4e, 0x58, 0x3f, 0x17, 0xb9, - 0xf4, 0x31, 0xca, 0x11, 0x77, 0xb4, 0x77, 0xac, 0x56, 0xc4, 0x96, 0xd4, 0x3a, 0x0b, 0x62, 0x34, 0xf3, 0xd9, 0x5d, - 0xca, 0x3f, 0x5f, 0xa8, 0x4c, 0x55, 0x71, 0xcb, 0x51, 0x0b, 0xe0, 0x1f, 0x78, 0x84, 0x24, 0x88, 0x0b, 0xd8, 0xe7, - 0x44, 0x78, 0xcf, 0x6a, 0x75, 0x22, 0xb6, 0x54, 0xac, 0x4e, 0x63, 0xe7, 0x51, 0x78, 0x3b, 0x84, 0xd1, 0x62, 0x63, - 0x33, 0x66, 0xfe, 0x0c, 0xdf, 0x98, 0xe8, 0x94, 0x29, 0xfa, 0x31, 0x51, 0x54, 0x03, 0xbd, 0xb1, 0x65, 0x1f, 0x5e, - 0xf7, 0x5a, 0x8a, 0xdd, 0x5f, 0x7a, 0x81, 0x49, 0xd3, 0x39, 0xb6, 0x57, 0x52, 0x5f, 0x32, 0xfc, 0xf4, 0x0d, 0x56, - 0x77, 0x14, 0xbb, 0x0f, 0x2c, 0xf9, 0xcc, 0x0f, 0x6f, 0x7b, 0x0b, 0x6f, 0x3a, 0x65, 0x41, 0x1f, 0xc7, 0x9c, 0x15, - 0x32, 0xdf, 0xf7, 0x56, 0xb1, 0x17, 0xf7, 0x97, 0xee, 0x1d, 0xef, 0xf5, 0xb0, 0xa9, 0xd7, 0x36, 0xef, 0xb5, 0xbd, - 0x77, 0xaf, 0x52, 0x37, 0xe0, 0x00, 0x4a, 0xfd, 0xf0, 0xa1, 0x75, 0x14, 0xbb, 0x34, 0xcf, 0xbd, 0x7b, 0x5d, 0x45, - 0x6c, 0xb3, 0x74, 0xa3, 0xb9, 0x17, 0xf4, 0xec, 0xd4, 0xba, 0xd9, 0xd0, 0xc6, 0x78, 0xdc, 0xed, 0x76, 0x53, 0x6b, - 0x2a, 0x9e, 0xec, 0xe9, 0x34, 0xb5, 0x26, 0xe2, 0x69, 0x36, 0xb3, 0xed, 0xd9, 0x2c, 0xb5, 0x3c, 0x51, 0xd0, 0x6e, - 0x4d, 0xa6, 0xed, 0x56, 0x6a, 0xdd, 0x4a, 0x35, 0x52, 0x8b, 0xf1, 0xa7, 0x88, 0x4d, 0xfb, 0xb8, 0x91, 0xb8, 0x3d, - 0xf9, 0xb1, 0x6d, 0xa7, 0x88, 0x01, 0x2e, 0x0b, 0xb8, 0x09, 0xa5, 0x81, 0x57, 0x9b, 0xbd, 0x6b, 0x2a, 0xf9, 0xe7, - 0x26, 0x93, 0xda, 0x7a, 0x53, 0x37, 0xfa, 0x70, 0xa5, 0x48, 0xb3, 0x70, 0x5d, 0xaa, 0xb6, 0x11, 0x60, 0x30, 0xef, - 0x7a, 0x10, 0xed, 0xb2, 0x3f, 0x0e, 0x23, 0x38, 0xb3, 0x91, 0x3b, 0xf5, 0xd6, 0x71, 0xcf, 0x69, 0xad, 0xee, 0x44, - 0x11, 0xdf, 0xeb, 0x79, 0x01, 0x9e, 0xbd, 0x5e, 0x1c, 0xfa, 0xde, 0x54, 0x14, 0x35, 0x9d, 0x25, 0xa7, 0xa5, 0xf7, - 0x31, 0xd6, 0x8b, 0x87, 0x11, 0x8b, 0x5c, 0xdf, 0x57, 0xac, 0x76, 0xac, 0x30, 0x37, 0x46, 0x0d, 0x84, 0x62, 0xc7, - 0x04, 0x17, 0x8c, 0xeb, 0xe2, 0x1c, 0xae, 0xee, 0xb2, 0x3d, 0xef, 0x1c, 0xad, 0xee, 0xd2, 0xaf, 0x96, 0x6c, 0xea, - 0xb9, 0x8a, 0x96, 0xef, 0x26, 0xc7, 0x06, 0x2d, 0x85, 0xbe, 0x69, 0xd8, 0xa6, 0xe2, 0x58, 0x40, 0x54, 0xe0, 0x47, - 0xde, 0x72, 0x15, 0x46, 0x89, 0x1b, 0x24, 0x69, 0x3a, 0xba, 0x4a, 0xd3, 0xfe, 0x85, 0xa7, 0x5d, 0xfe, 0x43, 0xa3, - 0x7b, 0x9a, 0xb4, 0x7a, 0xa9, 0x7e, 0x65, 0xbc, 0x61, 0xb2, 0x05, 0x12, 0x5c, 0x63, 0x68, 0x7d, 0x24, 0x57, 0xa6, - 0x5b, 0xb2, 0x5a, 0x99, 0x80, 0x9c, 0x55, 0x27, 0x83, 0xa6, 0x62, 0x15, 0xbc, 0x81, 0xa0, 0xc2, 0x1b, 0x36, 0xb8, - 0x90, 0xcc, 0x99, 0x80, 0x58, 0xc1, 0xca, 0xe4, 0x92, 0xf7, 0xa4, 0x89, 0x66, 0xfc, 0x7a, 0x37, 0xcd, 0xf8, 0xcf, - 0x64, 0x1f, 0x9a, 0xf1, 0xeb, 0xcf, 0x4e, 0x33, 0x3e, 0xa9, 0xba, 0xe4, 0x9d, 0x85, 0x03, 0x35, 0xd3, 0x41, 0xc1, - 0xd5, 0x14, 0x51, 0xb0, 0xbb, 0xb3, 0xe4, 0xbf, 0x75, 0xa1, 0x13, 0xbd, 0x51, 0xfa, 0x56, 0xba, 0xb9, 0x81, 0xf0, - 0x7e, 0x1b, 0x0c, 0xfe, 0x1e, 0xc9, 0xcf, 0xb3, 0xd9, 0xe0, 0x75, 0x28, 0x15, 0x64, 0x4f, 0xdc, 0x3c, 0xa7, 0x10, - 0x98, 0x88, 0xde, 0x64, 0x06, 0x54, 0x90, 0xba, 0x09, 0xe2, 0x9a, 0x90, 0x91, 0xfc, 0x34, 0x33, 0x63, 0xec, 0x17, - 0x87, 0xa0, 0x65, 0x86, 0xc1, 0xc2, 0x7b, 0xb5, 0x22, 0x6c, 0x9e, 0xb3, 0x84, 0x87, 0x9b, 0x78, 0x79, 0x7f, 0x36, - 0xd5, 0xce, 0x42, 0x3d, 0xf5, 0xe2, 0xb7, 0x65, 0xdf, 0x51, 0xc1, 0x3a, 0xc8, 0xd3, 0x49, 0xb9, 0x29, 0xa2, 0x14, - 0x22, 0x06, 0x5f, 0x53, 0xf3, 0xd3, 0xc2, 0x4c, 0x7b, 0x72, 0x43, 0x9e, 0x23, 0xb2, 0x72, 0x19, 0x73, 0x57, 0xbc, - 0x0d, 0xa7, 0x00, 0x31, 0xed, 0x25, 0x86, 0xdc, 0x98, 0x52, 0x73, 0x6f, 0x9a, 0xa6, 0x7a, 0x5f, 0x00, 0x42, 0xba, - 0x68, 0xd9, 0x2e, 0x22, 0x2e, 0xce, 0x19, 0x51, 0xae, 0x43, 0x26, 0x05, 0xf1, 0x19, 0x98, 0x5c, 0x70, 0x75, 0x32, - 0x87, 0x99, 0xaa, 0x10, 0xf8, 0xc8, 0x14, 0x47, 0x9a, 0x10, 0xd8, 0x08, 0xc8, 0x06, 0x6c, 0x85, 0x05, 0xa9, 0x1a, - 0x03, 0x13, 0x70, 0xd0, 0x66, 0x04, 0x02, 0xe4, 0x08, 0x29, 0x15, 0xa1, 0x1d, 0x5e, 0x07, 0x1f, 0x52, 0x35, 0xa3, - 0xfd, 0x70, 0xfb, 0x0d, 0x4f, 0x0e, 0xa0, 0xc1, 0xb0, 0x24, 0x81, 0xda, 0x61, 0xea, 0x0a, 0xa4, 0x44, 0x7c, 0x6b, - 0x58, 0xf1, 0xad, 0xf2, 0x6c, 0x23, 0x82, 0x4b, 0x25, 0xee, 0xca, 0x04, 0xb1, 0x07, 0xe2, 0x5e, 0x8e, 0xf1, 0xa4, - 0x38, 0x56, 0xfd, 0x75, 0x0c, 0xb8, 0x11, 0x39, 0x70, 0xc4, 0x3f, 0xfd, 0xc9, 0x3a, 0x8a, 0xc3, 0xa8, 0xb7, 0x0a, - 0xbd, 0x20, 0x61, 0x51, 0x8a, 0xa0, 0xba, 0x44, 0xf8, 0x08, 0xf0, 0x5c, 0x6d, 0xc2, 0x95, 0x3b, 0xf1, 0x92, 0xfb, - 0x9e, 0xcd, 0x49, 0x0a, 0xbb, 0xcf, 0xa9, 0x03, 0xbb, 0xb6, 0x7e, 0x8f, 0x43, 0xf3, 0x39, 0x12, 0x7e, 0x51, 0x95, - 0x9c, 0x91, 0xb7, 0x79, 0x5f, 0x7a, 0x4b, 0xe1, 0xb5, 0x80, 0xfc, 0x70, 0x23, 0x73, 0x0e, 0x58, 0x1e, 0x96, 0xda, - 0x9e, 0xb2, 0xb9, 0x81, 0x58, 0x1b, 0x34, 0x37, 0xe2, 0x8f, 0xd5, 0xd1, 0x15, 0xbb, 0xbe, 0x18, 0x28, 0x1e, 0x7d, - 0x9f, 0x91, 0xf5, 0x5c, 0x48, 0x46, 0x69, 0xec, 0x53, 0x73, 0xcc, 0x66, 0x61, 0xc4, 0x28, 0x14, 0xbb, 0xd3, 0x5d, - 0xdd, 0xed, 0xdf, 0xfd, 0xf6, 0xe9, 0xd7, 0xf7, 0x13, 0x84, 0x89, 0x26, 0x3a, 0xd3, 0x77, 0xf4, 0x56, 0xbd, 0xcf, - 0x80, 0x34, 0x24, 0xc8, 0x4f, 0xc8, 0x51, 0xa4, 0xa7, 0xaa, 0xfd, 0xda, 0x88, 0x97, 0xab, 0x90, 0xdf, 0x79, 0x11, - 0xf3, 0xdd, 0xc4, 0xbb, 0x11, 0x34, 0x63, 0xfb, 0x68, 0x75, 0x27, 0xd6, 0x18, 0x2f, 0xbc, 0x07, 0x2c, 0x52, 0x69, - 0x28, 0x62, 0x91, 0xca, 0xc5, 0xb8, 0x48, 0xfd, 0xca, 0x6c, 0x44, 0x10, 0xa8, 0xd2, 0x4d, 0xdf, 0x59, 0xdd, 0xc9, - 0x57, 0x74, 0xde, 0x2c, 0xbb, 0xa9, 0xcb, 0xd1, 0x3b, 0x97, 0xde, 0x74, 0xea, 0xb3, 0xb4, 0xb0, 0xd0, 0xc5, 0xb5, - 0x94, 0x80, 0x93, 0xc1, 0xc1, 0x1d, 0xc7, 0xa1, 0xbf, 0x4e, 0x58, 0x3d, 0xb8, 0x08, 0x38, 0x2d, 0x3b, 0x07, 0x0e, - 0xfe, 0x2e, 0x8e, 0xb5, 0x03, 0xe4, 0x36, 0x6c, 0x13, 0xbb, 0x0f, 0xc1, 0xfa, 0xcd, 0x76, 0x71, 0xe8, 0xf0, 0x2a, - 0x1b, 0xb4, 0x51, 0x33, 0x11, 0x03, 0xae, 0x25, 0xc2, 0xde, 0x8a, 0xe5, 0xf0, 0xb2, 0x2c, 0x60, 0x79, 0x56, 0x94, - 0x16, 0x27, 0xf3, 0xfb, 0x9c, 0xb1, 0x17, 0xf5, 0x67, 0xec, 0x85, 0x38, 0x63, 0xdb, 0x77, 0xe6, 0xe3, 0x99, 0x03, - 0xff, 0xf5, 0xf3, 0x09, 0xf5, 0x6c, 0xa5, 0xbd, 0xba, 0x53, 0x9c, 0xd5, 0x9d, 0x62, 0xb6, 0x56, 0x77, 0x0a, 0x76, - 0x8d, 0x16, 0x43, 0x86, 0xd5, 0xd2, 0x0d, 0x5b, 0x81, 0x42, 0xf8, 0x63, 0x17, 0x5e, 0x39, 0x87, 0xf0, 0x0e, 0x5a, - 0x75, 0xaa, 0xef, 0x5a, 0xdb, 0x8f, 0x3a, 0x9d, 0x25, 0x81, 0xb4, 0x75, 0x2b, 0x71, 0xc7, 0x63, 0x36, 0xed, 0xcd, - 0xc2, 0xc9, 0x3a, 0xfe, 0x37, 0x1f, 0x3f, 0x07, 0xe2, 0x56, 0x44, 0x50, 0xea, 0x47, 0x34, 0x05, 0xa9, 0xdc, 0x0d, - 0x13, 0x3d, 0x6c, 0xb2, 0x75, 0xea, 0x51, 0x66, 0x81, 0x96, 0x75, 0x58, 0xb3, 0xc9, 0xeb, 0x01, 0xfd, 0xbb, 0xad, - 0x52, 0x33, 0x8a, 0xf9, 0x04, 0xb0, 0x6c, 0x05, 0xc7, 0xc3, 0xa1, 0xc1, 0x57, 0xd3, 0xee, 0xd6, 0x0f, 0xf7, 0x52, - 0x7c, 0xe9, 0x4a, 0x5c, 0x2a, 0xfc, 0xde, 0xe2, 0xee, 0x4b, 0xdb, 0x7b, 0x6d, 0xda, 0x23, 0x95, 0x5e, 0xb7, 0x5c, - 0x08, 0x79, 0xdd, 0x3d, 0xb1, 0xfc, 0xe3, 0x17, 0x87, 0xf0, 0x1f, 0x51, 0xf5, 0xff, 0x4c, 0xea, 0x08, 0xf5, 0xb3, - 0xa4, 0x40, 0xa8, 0x13, 0xa9, 0x84, 0x84, 0xf8, 0xfe, 0xf5, 0x67, 0xb3, 0x87, 0x35, 0xd8, 0xbb, 0x36, 0x19, 0xdb, - 0x95, 0x6b, 0xbf, 0x0c, 0x43, 0xc8, 0x7a, 0x5d, 0xad, 0x2e, 0xc0, 0x43, 0x9e, 0x13, 0xc9, 0x00, 0x1a, 0x09, 0x3e, - 0x82, 0xec, 0x3c, 0x54, 0x6c, 0x43, 0xac, 0xc4, 0x9b, 0x26, 0x56, 0xe2, 0xf5, 0x6e, 0x56, 0xe2, 0xbb, 0xbd, 0x58, - 0x89, 0xd7, 0x9f, 0x9d, 0x95, 0x78, 0x53, 0x65, 0x25, 0x2e, 0x42, 0x61, 0x61, 0x6d, 0x9c, 0xad, 0xf9, 0xcf, 0x9f, - 0x49, 0x85, 0x7a, 0x1e, 0x0e, 0x3a, 0x36, 0x65, 0x0b, 0xb8, 0xf8, 0xaf, 0x19, 0x0b, 0xdc, 0x88, 0xef, 0xd0, 0xe0, - 0x30, 0x67, 0x2d, 0x38, 0x66, 0xc7, 0xef, 0x48, 0xc5, 0x7e, 0x18, 0xcc, 0x7f, 0x04, 0x15, 0x3a, 0x88, 0x03, 0x23, - 0xe9, 0x85, 0x17, 0xff, 0x18, 0xae, 0xd6, 0xab, 0x33, 0xe8, 0xeb, 0x67, 0x2f, 0xf6, 0xc6, 0x3e, 0xcb, 0xa2, 0x78, - 0x90, 0x81, 0x24, 0x97, 0x89, 0x83, 0x4d, 0xb2, 0xf8, 0xe9, 0xde, 0x89, 0x9f, 0x68, 0xb5, 0xcc, 0x7f, 0x93, 0xe5, - 0xa5, 0x5a, 0xcf, 0x88, 0x40, 0xb8, 0xbb, 0xd2, 0xa0, 0x1f, 0xce, 0x8c, 0x5c, 0x84, 0x7a, 0xcd, 0x2c, 0x85, 0x45, - 0x4c, 0x63, 0x3f, 0xac, 0xc2, 0xd4, 0xac, 0x75, 0x23, 0x8b, 0x5e, 0x59, 0x15, 0xc3, 0x2f, 0xc3, 0x75, 0xcc, 0xa6, - 0xe1, 0x6d, 0xa0, 0x1a, 0x01, 0x37, 0xe3, 0xa4, 0x04, 0x80, 0x59, 0x1b, 0xcc, 0xbb, 0xfc, 0x1e, 0x09, 0x65, 0x88, - 0x74, 0x00, 0x69, 0xbf, 0xd7, 0x2b, 0x93, 0x0c, 0x03, 0x4c, 0x9c, 0xa2, 0x9a, 0x25, 0x08, 0x7c, 0xa4, 0x69, 0xe1, - 0xe0, 0x61, 0x2d, 0x85, 0x31, 0x4f, 0x68, 0x71, 0xa9, 0x70, 0xac, 0x05, 0x42, 0xb8, 0x28, 0x42, 0x48, 0xd5, 0x2c, - 0x1c, 0x7f, 0x43, 0x21, 0x3a, 0xf2, 0xb7, 0x10, 0xd1, 0x21, 0x5d, 0xf3, 0xf5, 0xe0, 0x01, 0x95, 0xe8, 0xf1, 0x95, - 0x04, 0xc6, 0xb7, 0x37, 0x2c, 0xf2, 0xdd, 0x7b, 0x4d, 0x4f, 0xc3, 0xe0, 0x7b, 0x00, 0xc0, 0xeb, 0xf0, 0x36, 0x90, - 0x2b, 0x60, 0x9e, 0xb3, 0x9a, 0xbd, 0x54, 0x1b, 0xfa, 0x0b, 0xdc, 0xa0, 0xa4, 0x11, 0x40, 0x86, 0xf9, 0x39, 0xfb, - 0xbb, 0x41, 0xff, 0xfe, 0x43, 0x4f, 0x8d, 0xf3, 0x30, 0xfb, 0xd0, 0x4f, 0xab, 0x3d, 0x3e, 0xf3, 0xf4, 0xe9, 0xa3, - 0xe6, 0x69, 0x6b, 0x13, 0x9f, 0xb9, 0x22, 0x6f, 0xbc, 0x56, 0xd3, 0x5a, 0x6f, 0x3c, 0x05, 0x30, 0x8a, 0x8b, 0x70, - 0x3d, 0x59, 0xa0, 0x29, 0xf4, 0xe7, 0x9b, 0x6f, 0x02, 0x7d, 0x62, 0x82, 0xef, 0x6c, 0xea, 0xa5, 0xa2, 0x1c, 0x0a, - 0xf8, 0xfd, 0x37, 0x10, 0xbb, 0xfa, 0x4f, 0x04, 0x43, 0x75, 0xd7, 0x64, 0x6e, 0xd2, 0x0f, 0xda, 0xbc, 0x7d, 0xc8, - 0x43, 0xcd, 0xa3, 0x42, 0x09, 0xe5, 0x5a, 0x3d, 0x92, 0x49, 0xcb, 0x40, 0x93, 0x23, 0xb0, 0x36, 0x05, 0x97, 0x15, - 0x5f, 0x61, 0x16, 0xb1, 0xe9, 0xdc, 0x15, 0xc5, 0x60, 0x1c, 0x5b, 0x95, 0x90, 0x0c, 0x37, 0x50, 0x61, 0x88, 0xbe, - 0xca, 0xef, 0x96, 0x5e, 0x60, 0x60, 0x02, 0x95, 0xea, 0x1b, 0xf7, 0x0e, 0x52, 0x08, 0x00, 0x72, 0x2b, 0xbf, 0x82, - 0x42, 0x43, 0x76, 0xc0, 0x84, 0x2c, 0x89, 0x6a, 0x2d, 0x24, 0x84, 0x16, 0x6f, 0xf4, 0x85, 0xa2, 0x28, 0x4a, 0xc6, - 0x46, 0x28, 0x19, 0x1f, 0x81, 0xe5, 0xc8, 0x0e, 0x80, 0xb6, 0x24, 0x5d, 0xdd, 0x51, 0x09, 0x70, 0x06, 0xa8, 0x92, - 0x16, 0x05, 0x3c, 0x4a, 0x6e, 0xc7, 0x16, 0x05, 0x82, 0xa1, 0x87, 0x08, 0xa7, 0x6e, 0x04, 0xc1, 0xf4, 0x7b, 0x0a, - 0x32, 0xec, 0xf8, 0x96, 0x4b, 0x82, 0x15, 0x9b, 0x1e, 0x47, 0x7d, 0x56, 0x1f, 0x4e, 0x35, 0x90, 0xb0, 0x20, 0x68, - 0x1d, 0x4a, 0xd9, 0x11, 0x0c, 0x56, 0x83, 0x1b, 0x91, 0x2f, 0xba, 0x4b, 0x96, 0x2c, 0x58, 0xab, 0x98, 0x4e, 0x11, - 0xc3, 0xdb, 0x42, 0x9d, 0xd7, 0x44, 0x6c, 0x01, 0xb6, 0xa9, 0x6f, 0xb9, 0xa0, 0xbb, 0x30, 0xe6, 0x28, 0xd5, 0x35, - 0x26, 0x5c, 0xb1, 0x19, 0x73, 0xdc, 0x56, 0xbe, 0x21, 0xf8, 0x92, 0x86, 0x45, 0x6c, 0xce, 0xbd, 0x88, 0x91, 0x52, - 0xa0, 0xc8, 0x52, 0x5c, 0x5c, 0x24, 0xc0, 0xae, 0xb9, 0xe5, 0x45, 0xcb, 0x34, 0x32, 0x6e, 0x49, 0x50, 0x14, 0xe9, - 0xd5, 0x6e, 0xf8, 0x38, 0x21, 0xa6, 0x5f, 0x63, 0x3f, 0x93, 0x4a, 0x3f, 0x0d, 0x93, 0xfe, 0xc0, 0xee, 0xe9, 0x22, - 0x21, 0x50, 0x7d, 0x60, 0xf7, 0xa0, 0x6f, 0x7f, 0x03, 0xd2, 0x14, 0x75, 0x0b, 0xba, 0x36, 0x20, 0x4b, 0xce, 0x04, - 0xe2, 0x3c, 0x6e, 0x39, 0x40, 0x76, 0xba, 0x05, 0x8b, 0x23, 0x88, 0x03, 0x23, 0xee, 0x8b, 0x43, 0xcc, 0x9d, 0x40, - 0xb4, 0x5a, 0x18, 0x9b, 0x35, 0x47, 0x43, 0x7f, 0xe6, 0xd8, 0xf6, 0x41, 0xa5, 0x3e, 0x08, 0xb2, 0xeb, 0x6a, 0xeb, - 0x46, 0x32, 0x70, 0x6c, 0xd3, 0x7b, 0x66, 0xb5, 0xfa, 0x95, 0x3b, 0x5a, 0x0a, 0xc3, 0x3c, 0x42, 0xf1, 0xd7, 0xf0, - 0xc9, 0x46, 0xab, 0x1c, 0x48, 0xbd, 0xec, 0x54, 0x81, 0x63, 0x4b, 0xb9, 0xfc, 0x6b, 0x54, 0xbd, 0xfa, 0x29, 0x08, - 0x34, 0xa5, 0x04, 0x1b, 0x41, 0x22, 0x01, 0x0d, 0x8e, 0xd1, 0x5f, 0x94, 0xe7, 0x8a, 0x46, 0xc7, 0x47, 0xd7, 0x47, - 0x7d, 0x81, 0x51, 0x84, 0xd7, 0xa1, 0xdc, 0x41, 0xe9, 0x8b, 0x71, 0x19, 0xc3, 0xf1, 0x90, 0xe5, 0x2c, 0xd7, 0xe8, - 0x6d, 0xa5, 0x16, 0xb0, 0xff, 0x86, 0xeb, 0xd3, 0x1a, 0x43, 0x5c, 0x0c, 0xa8, 0x01, 0x69, 0x47, 0x76, 0x76, 0x08, - 0xe1, 0x8e, 0xe4, 0xee, 0x8a, 0x97, 0xe4, 0xfe, 0x9d, 0xe1, 0xa5, 0x83, 0x3a, 0xb4, 0xac, 0xbf, 0xfa, 0xeb, 0xee, - 0x81, 0x5d, 0xb2, 0x60, 0x5a, 0xec, 0xb0, 0x74, 0x7f, 0xed, 0xdf, 0x5d, 0x01, 0xa3, 0x40, 0x3e, 0x9e, 0xb0, 0x06, - 0xa3, 0xa4, 0x61, 0x80, 0x9b, 0x9f, 0x8e, 0x9b, 0xb7, 0x17, 0x15, 0x83, 0x0d, 0x28, 0x98, 0x66, 0xd6, 0x4c, 0x12, - 0x8a, 0x43, 0xd2, 0x07, 0x74, 0x4a, 0xd6, 0x04, 0x21, 0xda, 0xb8, 0x13, 0x13, 0x61, 0x01, 0x9a, 0xb7, 0xf1, 0x78, - 0x18, 0xe7, 0x7d, 0xa5, 0xd6, 0xde, 0x6e, 0xa9, 0x75, 0xb2, 0x4b, 0x6a, 0x4d, 0x0e, 0x77, 0x64, 0xb6, 0x94, 0x39, - 0x1e, 0x0a, 0xe2, 0x5c, 0x76, 0xdd, 0x2c, 0x88, 0xba, 0xd1, 0x3f, 0x4f, 0xb4, 0xaa, 0xf4, 0x46, 0x36, 0x9d, 0x28, - 0xfe, 0x96, 0x18, 0x14, 0xa1, 0x50, 0x97, 0x65, 0xe3, 0x17, 0xb9, 0x6c, 0x9c, 0xb8, 0x9a, 0xdc, 0xd5, 0x4a, 0x50, - 0xff, 0x92, 0x1b, 0x63, 0xc6, 0x1d, 0xe4, 0xee, 0x8c, 0xf9, 0x48, 0x25, 0x07, 0xbd, 0x9c, 0xd1, 0x90, 0xdc, 0x3e, - 0x05, 0x97, 0x51, 0xf4, 0xfe, 0x2c, 0x56, 0xcd, 0xfd, 0xf3, 0xf2, 0x72, 0x90, 0xba, 0xe3, 0x90, 0xb3, 0x62, 0x79, - 0xdb, 0x14, 0x1d, 0xb4, 0xe4, 0xd7, 0xd2, 0x26, 0xc9, 0x3c, 0xa9, 0x08, 0xc0, 0x42, 0x4c, 0x5f, 0xd2, 0x6b, 0x67, - 0x36, 0x10, 0x38, 0xc8, 0x1a, 0xc7, 0xcf, 0xdd, 0xd2, 0x79, 0x4a, 0x35, 0x94, 0xab, 0xae, 0x1d, 0xbc, 0xdd, 0x49, - 0x13, 0x2c, 0xcb, 0x23, 0x10, 0xd6, 0x57, 0x92, 0x04, 0xa1, 0x67, 0x2b, 0x76, 0xbf, 0x86, 0x00, 0xc0, 0xfb, 0xbf, - 0xfc, 0xcc, 0x49, 0x01, 0x90, 0x44, 0x2a, 0xb6, 0xac, 0xf3, 0xc7, 0x43, 0x6c, 0x92, 0xd9, 0x18, 0x56, 0xad, 0x7e, - 0x93, 0xe4, 0x3d, 0x1b, 0xee, 0x66, 0x55, 0x14, 0xe7, 0xf3, 0x1a, 0x3d, 0x31, 0x0e, 0xbe, 0xcb, 0xa2, 0x75, 0x80, - 0x19, 0x64, 0xcc, 0x24, 0x72, 0x27, 0x1f, 0x36, 0xd2, 0xf7, 0xb8, 0x48, 0x14, 0xc4, 0xc5, 0x45, 0xa5, 0x42, 0xdf, - 0xc5, 0x80, 0xcb, 0xac, 0x67, 0xb5, 0x62, 0x49, 0x50, 0xd3, 0x7b, 0x6c, 0xb7, 0xdd, 0x17, 0xb3, 0xc3, 0x92, 0xfc, - 0xb4, 0xd5, 0x29, 0x4a, 0xd7, 0xb3, 0x71, 0x2c, 0xc3, 0x5f, 0xb9, 0x43, 0xea, 0x1f, 0xff, 0xe9, 0x98, 0x7f, 0xb3, - 0xb4, 0x46, 0x9f, 0x32, 0x04, 0x68, 0x5f, 0x50, 0x4c, 0xcb, 0x6a, 0x9a, 0x4a, 0x49, 0xd3, 0xb0, 0x66, 0x9e, 0xef, - 0x9b, 0x3e, 0xb8, 0x05, 0x6d, 0x3e, 0x69, 0x7a, 0xd8, 0xcf, 0x1a, 0x42, 0xfd, 0x7f, 0x42, 0x3f, 0xc5, 0x9d, 0x92, - 0x2c, 0xd6, 0xcb, 0xf1, 0x46, 0x16, 0x94, 0x4b, 0xf2, 0xf3, 0xaa, 0xcc, 0x5c, 0xfe, 0xec, 0x6c, 0x36, 0x2b, 0x4a, - 0x8d, 0x6d, 0xe5, 0x10, 0x25, 0xbf, 0x8f, 0x6d, 0xdb, 0x2e, 0xc3, 0xb7, 0xe9, 0xa0, 0xd0, 0xc1, 0x30, 0x51, 0x08, - 0xdf, 0xdd, 0xbd, 0xa7, 0xfe, 0xa0, 0xd1, 0x52, 0x57, 0x4d, 0xe7, 0x91, 0xb6, 0xda, 0xff, 0x8b, 0xa1, 0x20, 0x6a, - 0xd8, 0x75, 0xfc, 0xab, 0x7b, 0x65, 0x4b, 0x4f, 0xe5, 0x03, 0xfc, 0xb0, 0xc6, 0x3b, 0xf6, 0xfa, 0x1e, 0x4d, 0x9b, - 0xb6, 0x77, 0x6a, 0xe5, 0xd7, 0x6e, 0xc1, 0x66, 0xa9, 0x4f, 0x96, 0x4a, 0x5e, 0xc2, 0x96, 0x71, 0x6f, 0xc2, 0x50, - 0x41, 0x6a, 0x49, 0xb7, 0x2d, 0x5a, 0xf5, 0x98, 0x73, 0xb0, 0xe3, 0x72, 0x04, 0x1e, 0xb6, 0x15, 0x54, 0x56, 0x55, - 0x34, 0x6b, 0xe2, 0x23, 0x78, 0x8b, 0x6d, 0xaa, 0x0a, 0x27, 0xdc, 0xa6, 0x1d, 0xfb, 0x2f, 0x85, 0x7a, 0x0a, 0x50, - 0xa7, 0x1b, 0x61, 0x6d, 0x42, 0xca, 0x13, 0xfc, 0x3b, 0x53, 0xce, 0xbd, 0x58, 0xdd, 0x15, 0x8d, 0xbb, 0xba, 0xa0, - 0x6e, 0xca, 0xaf, 0x32, 0x1a, 0x75, 0x1d, 0xea, 0xcb, 0x4c, 0x80, 0x66, 0xb2, 0x75, 0x0b, 0x58, 0xd0, 0x14, 0x12, - 0xdb, 0xd5, 0xe8, 0xc6, 0x90, 0x9d, 0x85, 0x9d, 0x97, 0xcb, 0xf7, 0xf3, 0xb4, 0xcc, 0x30, 0x07, 0xe3, 0x79, 0x17, - 0x95, 0x7b, 0x85, 0xad, 0x8a, 0xa6, 0x32, 0xb8, 0x07, 0x04, 0x47, 0xaa, 0xac, 0x23, 0xdf, 0xa4, 0xac, 0x6f, 0x9a, - 0xbe, 0xa9, 0xce, 0xbb, 0xb9, 0x7b, 0xa7, 0x03, 0x7a, 0x8d, 0x2a, 0xa8, 0xf6, 0x52, 0xed, 0x95, 0x75, 0xd8, 0x62, - 0x9c, 0xb0, 0x02, 0xe0, 0x42, 0xa2, 0xa0, 0xd1, 0x90, 0x52, 0xc2, 0x7d, 0x34, 0xe9, 0xec, 0xad, 0x8c, 0xac, 0xc5, - 0x3c, 0xb1, 0xbb, 0xfa, 0x2a, 0xd4, 0xb7, 0xd0, 0x0c, 0x02, 0xec, 0x38, 0x76, 0xc2, 0x67, 0x13, 0x76, 0x8c, 0x8c, - 0xae, 0x1c, 0xdc, 0x41, 0x78, 0x4a, 0x4d, 0x8a, 0x4b, 0x4b, 0xa7, 0x14, 0x75, 0x09, 0xdf, 0xd5, 0x0a, 0xef, 0x2f, - 0x0a, 0xd2, 0x78, 0xee, 0xc7, 0xd3, 0xd2, 0xf7, 0xaa, 0xbd, 0xf4, 0x82, 0xfd, 0xeb, 0xba, 0x77, 0x7b, 0xd7, 0x05, - 0xe2, 0x70, 0xef, 0xca, 0x40, 0x5d, 0x92, 0x95, 0x52, 0x32, 0xf8, 0x4e, 0x52, 0x1e, 0xc8, 0x31, 0x28, 0x54, 0x6c, - 0x45, 0x1c, 0xfd, 0xc5, 0x7a, 0x30, 0x3a, 0x39, 0xbd, 0x5b, 0xfa, 0xca, 0x0d, 0x8b, 0x20, 0x03, 0xe6, 0x40, 0x75, - 0x2c, 0x5b, 0x55, 0x30, 0xa2, 0x82, 0x17, 0xcc, 0x07, 0xea, 0x4f, 0x17, 0xdf, 0x98, 0x5d, 0xf5, 0x14, 0xcc, 0x31, - 0x6e, 0xe6, 0x48, 0xe2, 0x9e, 0xbb, 0xf7, 0x2c, 0xba, 0x6e, 0xa9, 0x0a, 0x26, 0xba, 0x24, 0xe2, 0x16, 0xcb, 0x94, - 0x96, 0xba, 0x47, 0x3e, 0x35, 0x45, 0xa4, 0x44, 0x56, 0x01, 0xb1, 0x3a, 0xad, 0xae, 0xe2, 0xb4, 0x0e, 0xad, 0xa3, - 0xae, 0x3a, 0xfc, 0x42, 0x51, 0x4e, 0xa6, 0x6c, 0x16, 0x0f, 0x51, 0x1c, 0x73, 0x82, 0xf4, 0x20, 0xfd, 0x56, 0x14, - 0x6b, 0xe2, 0xc7, 0xa6, 0xa3, 0x6c, 0xf8, 0xa3, 0xa2, 0x00, 0x32, 0xea, 0x29, 0x8f, 0x67, 0xad, 0xd9, 0xe1, 0xec, - 0x45, 0x9f, 0x17, 0xa7, 0x5f, 0x14, 0xaa, 0x1b, 0xf4, 0x6f, 0x4b, 0x6a, 0x16, 0x27, 0x51, 0xf8, 0x81, 0x71, 0x5a, - 0x52, 0xc9, 0x04, 0x45, 0xe5, 0xa6, 0xad, 0xea, 0x97, 0x9c, 0xee, 0x78, 0x32, 0x6b, 0xe5, 0xd5, 0x71, 0x8c, 0x07, - 0xd9, 0x20, 0x4f, 0x0e, 0xc4, 0xd0, 0x4f, 0x64, 0x30, 0x39, 0x66, 0x1d, 0xa0, 0x1c, 0x95, 0xcf, 0x71, 0x2e, 0xe6, - 0x77, 0x02, 0xe1, 0xca, 0x73, 0xcf, 0x8b, 0x18, 0x9b, 0x0d, 0xd4, 0xef, 0x9d, 0x56, 0xd7, 0x70, 0x9c, 0x23, 0xeb, - 0xa8, 0x3b, 0xb1, 0x8d, 0x43, 0xeb, 0xd0, 0x6c, 0x5b, 0x47, 0x46, 0xd7, 0xec, 0x1a, 0xdd, 0x6f, 0xbb, 0x13, 0xf3, - 0xd0, 0x3a, 0x34, 0x6c, 0xb3, 0x0b, 0x85, 0x66, 0xd7, 0xec, 0xde, 0x98, 0x87, 0xdd, 0x89, 0x8d, 0xa5, 0x2d, 0xab, - 0xd3, 0x31, 0x1d, 0xdb, 0xea, 0x74, 0x8c, 0x8e, 0x75, 0x74, 0x64, 0x3a, 0x6d, 0xeb, 0xe8, 0xe8, 0xbc, 0xd3, 0xb5, - 0xda, 0xf0, 0xae, 0xdd, 0x9e, 0xb4, 0x2d, 0xc7, 0x31, 0xe1, 0x2f, 0xa3, 0x6b, 0xb5, 0xe8, 0x87, 0xe3, 0x58, 0x6d, - 0xc7, 0xb0, 0xfd, 0x4e, 0xcb, 0x3a, 0x7a, 0x61, 0xe0, 0xdf, 0x58, 0xcd, 0xc0, 0xbf, 0xa0, 0x1b, 0xe3, 0x85, 0xd5, - 0x3a, 0xa2, 0x5f, 0xd8, 0xe1, 0xcd, 0x61, 0xf7, 0x9f, 0xea, 0x41, 0xe3, 0x1c, 0x1c, 0x9a, 0x43, 0xb7, 0x63, 0xb5, - 0xdb, 0xc6, 0xa1, 0x63, 0x75, 0xdb, 0x0b, 0xf3, 0xb0, 0x65, 0x1d, 0x1d, 0x4f, 0x4c, 0xc7, 0x3a, 0x3e, 0x36, 0x6c, - 0xb3, 0x6d, 0xb5, 0x0c, 0xc7, 0x3a, 0x6c, 0xe3, 0x8f, 0xb6, 0xd5, 0xba, 0x39, 0x7e, 0x61, 0x1d, 0x75, 0x16, 0x47, - 0xd6, 0xe1, 0xcf, 0x87, 0x5d, 0xab, 0xd5, 0x5e, 0xb4, 0x8f, 0xac, 0xd6, 0xf1, 0xcd, 0x91, 0x75, 0xb8, 0x30, 0x5b, - 0x47, 0x5b, 0x5b, 0x3a, 0x2d, 0x0b, 0x60, 0x84, 0xaf, 0xe1, 0x85, 0xc1, 0x5f, 0xc0, 0x9f, 0x05, 0xb6, 0xfd, 0x03, - 0xbb, 0x89, 0xab, 0x4d, 0x5f, 0x58, 0xdd, 0xe3, 0x09, 0x55, 0x87, 0x02, 0x53, 0xd4, 0x80, 0x26, 0x37, 0x26, 0x7d, - 0x16, 0xbb, 0x33, 0x45, 0x47, 0xe2, 0x0f, 0xff, 0xd8, 0x8d, 0x09, 0x1f, 0xa6, 0xef, 0xfe, 0xa9, 0xfd, 0x64, 0x4b, - 0x0e, 0x09, 0xde, 0xbf, 0xe0, 0xff, 0x50, 0x6e, 0xc4, 0x91, 0x71, 0xde, 0xa4, 0x94, 0x7c, 0xb7, 0x5b, 0x29, 0xf9, - 0xcd, 0x7a, 0x1f, 0xa5, 0xe4, 0xbb, 0xcf, 0xae, 0x94, 0x3c, 0x2f, 0xfb, 0xc4, 0xbc, 0x2b, 0xa7, 0x70, 0xfa, 0x6e, - 0x53, 0x16, 0x39, 0x78, 0xae, 0x76, 0x79, 0xb1, 0xbe, 0x82, 0x90, 0x7c, 0xef, 0xc2, 0xc1, 0x37, 0xeb, 0x82, 0xc1, - 0x67, 0x08, 0x38, 0xf6, 0x5d, 0x48, 0x38, 0xf6, 0x87, 0xf5, 0x00, 0xac, 0xcc, 0x38, 0x99, 0xe3, 0x4d, 0xcd, 0x85, - 0xeb, 0xcf, 0x32, 0x12, 0x09, 0x4a, 0xfa, 0x58, 0x0c, 0x0e, 0x67, 0x70, 0x3d, 0x03, 0x27, 0xb3, 0x5e, 0x06, 0x31, - 0x58, 0x04, 0x83, 0x25, 0xc7, 0x2c, 0x4a, 0x4b, 0x8d, 0x2d, 0x11, 0xc4, 0xf0, 0x9a, 0x7b, 0x2f, 0x35, 0xbe, 0x47, - 0x03, 0xe0, 0xfa, 0xde, 0x9d, 0x6a, 0xbf, 0x0a, 0x58, 0xd6, 0x09, 0x03, 0x69, 0xa0, 0xf6, 0xeb, 0xde, 0x17, 0xcd, - 0x70, 0x4b, 0x86, 0xd7, 0xcd, 0x23, 0x85, 0x91, 0x94, 0xdb, 0x3b, 0x45, 0x33, 0xde, 0x5d, 0xd3, 0xac, 0xf9, 0x7c, - 0xa1, 0xf9, 0x16, 0x1b, 0xe2, 0xac, 0xe3, 0x32, 0xa8, 0x4a, 0x09, 0x88, 0x6b, 0x01, 0x92, 0x33, 0xa8, 0xb9, 0xa1, - 0x71, 0x4e, 0xa9, 0xda, 0x0a, 0xd2, 0x3b, 0xb6, 0xf4, 0xae, 0xd0, 0xa7, 0x6c, 0x9c, 0xfc, 0x6c, 0x83, 0x7c, 0x85, - 0xf7, 0x2b, 0x50, 0xa2, 0x9c, 0xe2, 0x19, 0x87, 0x32, 0x9c, 0x37, 0x52, 0xbf, 0x24, 0x8d, 0x48, 0x17, 0xce, 0xa6, - 0x4a, 0x8b, 0x36, 0xba, 0x25, 0x38, 0x6c, 0x29, 0xa8, 0x20, 0xfc, 0x3c, 0x39, 0x01, 0xa4, 0xe4, 0xa8, 0x81, 0x7e, - 0x0e, 0xdb, 0x3a, 0x13, 0xf5, 0x1e, 0xc3, 0x26, 0xe6, 0xc1, 0x92, 0x15, 0x39, 0x4a, 0xcc, 0x66, 0xe6, 0x87, 0x6e, - 0xd2, 0x43, 0x32, 0x4d, 0x22, 0x79, 0x5b, 0xe8, 0xb1, 0xd0, 0xdf, 0x62, 0x4c, 0x27, 0x77, 0xcc, 0x3b, 0x41, 0xcf, - 0x87, 0x6d, 0xf6, 0x77, 0x99, 0xa3, 0xd8, 0xa6, 0x60, 0x8e, 0xe2, 0x74, 0x8e, 0x0d, 0xe7, 0xc8, 0xb0, 0x8e, 0x3b, - 0x7a, 0x2a, 0x0e, 0x9c, 0xdc, 0x65, 0x01, 0x20, 0xe0, 0x00, 0x91, 0x0d, 0xd3, 0x0b, 0xbc, 0xc4, 0x73, 0xfd, 0x14, - 0xe8, 0xe1, 0x22, 0x93, 0xf2, 0xaf, 0x75, 0x9c, 0xc0, 0x1c, 0x05, 0xd1, 0x8b, 0xce, 0x1f, 0xe6, 0x98, 0x25, 0xb7, - 0x8c, 0x05, 0x0d, 0x86, 0x31, 0x65, 0x5f, 0x92, 0xdf, 0xcf, 0xb2, 0x3e, 0x25, 0xab, 0xb5, 0x71, 0x12, 0xf0, 0xfd, - 0x21, 0x1c, 0x1f, 0xd2, 0x91, 0xf1, 0x6b, 0x13, 0xc2, 0xfd, 0xd7, 0x6e, 0x84, 0x9b, 0xb0, 0x7d, 0x10, 0xee, 0xbf, - 0x3e, 0x3b, 0xc2, 0xfd, 0x55, 0x46, 0xb8, 0x05, 0xbf, 0xbf, 0x5c, 0xc3, 0xf4, 0x1e, 0x9f, 0x35, 0x48, 0x7e, 0xf2, - 0x5c, 0x3d, 0x20, 0x02, 0x1e, 0xf2, 0x42, 0x88, 0xe8, 0x5b, 0x2f, 0x0b, 0x49, 0x36, 0x51, 0x00, 0x8a, 0x89, 0x35, - 0x28, 0xa1, 0x9f, 0x37, 0x18, 0x0c, 0xec, 0x2c, 0xa9, 0x1f, 0xbb, 0x55, 0xce, 0x82, 0xc4, 0xb7, 0xde, 0x71, 0x3e, - 0x12, 0x14, 0xba, 0xdf, 0x84, 0xd1, 0xd2, 0xc5, 0xa8, 0xad, 0x2a, 0x26, 0xe7, 0x86, 0x07, 0x1b, 0x9c, 0x68, 0x27, - 0x61, 0x30, 0xcd, 0xb4, 0x92, 0x6c, 0x70, 0x49, 0x14, 0xb7, 0x7a, 0xcf, 0xdc, 0x48, 0x35, 0xe8, 0x35, 0x2c, 0xee, - 0xb3, 0xb6, 0xfd, 0xac, 0x75, 0xf8, 0xec, 0xc8, 0x86, 0xff, 0x1d, 0xd6, 0x4e, 0x0d, 0x5e, 0x71, 0x19, 0x06, 0x90, - 0x1f, 0x50, 0xd4, 0x6c, 0xaa, 0x76, 0xcb, 0xd8, 0x87, 0xbc, 0xd6, 0x71, 0x7d, 0xa5, 0xa9, 0x7b, 0x9f, 0xd7, 0xa9, - 0xad, 0xb1, 0x08, 0xd7, 0xd2, 0xb0, 0x6a, 0x46, 0xe3, 0x05, 0x6b, 0x90, 0xb3, 0x4b, 0x35, 0xe4, 0xd7, 0x7c, 0xba, - 0xf9, 0xbc, 0x58, 0x3b, 0xbd, 0xca, 0x93, 0x90, 0x8a, 0x64, 0x88, 0x3b, 0x21, 0xc8, 0x55, 0x94, 0x36, 0xc6, 0xe9, - 0xc6, 0x4c, 0x11, 0x10, 0xa5, 0x3b, 0x4b, 0x1d, 0xe9, 0xd2, 0x02, 0x25, 0xd1, 0x3a, 0x98, 0x68, 0xf8, 0xd3, 0x1d, - 0xc7, 0x9a, 0x77, 0x10, 0x59, 0xfc, 0xc3, 0x3a, 0xae, 0x9a, 0x3b, 0xb4, 0xf3, 0x8c, 0x6d, 0xb1, 0x58, 0x15, 0xf7, - 0x59, 0x62, 0x44, 0xa8, 0xc7, 0xa6, 0xa5, 0x35, 0x07, 0xee, 0xb3, 0xac, 0xe1, 0xb3, 0xc4, 0x08, 0x9e, 0x83, 0xee, - 0x73, 0x60, 0x3f, 0x7d, 0x4a, 0xb5, 0x20, 0xfb, 0x39, 0x4d, 0xeb, 0x74, 0x92, 0x07, 0xfb, 0x54, 0xdc, 0x79, 0x48, - 0xf1, 0x3e, 0x7b, 0x13, 0x23, 0x7c, 0xfe, 0x7c, 0x38, 0x70, 0x74, 0xcc, 0x06, 0x2a, 0xb2, 0x7a, 0xf3, 0x44, 0xb3, - 0xe7, 0xfb, 0x19, 0x1a, 0xe9, 0xb5, 0x2e, 0xb0, 0x2b, 0xe0, 0x99, 0x6c, 0xe1, 0x8e, 0xc0, 0xb1, 0x17, 0x24, 0x7e, - 0x23, 0x83, 0x02, 0x57, 0x18, 0xfc, 0x88, 0x3a, 0x19, 0xd7, 0xd5, 0xb6, 0x6c, 0xcb, 0x56, 0xb3, 0x86, 0x33, 0x6f, - 0x3e, 0xd8, 0x84, 0x89, 0x0b, 0x29, 0x34, 0xfd, 0x70, 0x0e, 0x7e, 0x74, 0x89, 0x97, 0xf8, 0x90, 0x8f, 0x11, 0x1c, - 0xea, 0x96, 0xc4, 0x97, 0xa7, 0xdc, 0xbb, 0xc1, 0x8d, 0x3e, 0x60, 0x4e, 0x6e, 0xe1, 0x42, 0x8b, 0xf1, 0xe7, 0xbe, - 0x87, 0xcb, 0x50, 0x53, 0x35, 0x90, 0x0d, 0xb0, 0x28, 0x36, 0x65, 0x6f, 0xa1, 0x9e, 0x02, 0x6d, 0x74, 0x95, 0x4f, - 0x62, 0x16, 0xb9, 0x4b, 0xc8, 0x5d, 0xb4, 0x49, 0x0d, 0x8e, 0x69, 0x55, 0x8e, 0x6a, 0x15, 0xe7, 0xc5, 0x91, 0xa1, - 0xb4, 0x1c, 0x43, 0xb1, 0x01, 0xdd, 0xaa, 0xa9, 0xb1, 0x49, 0xaf, 0xfa, 0xbb, 0x0c, 0x1e, 0x08, 0xbf, 0x3c, 0xa6, - 0x79, 0x90, 0xa9, 0x03, 0x57, 0x25, 0x25, 0x14, 0x77, 0x58, 0x93, 0x32, 0x92, 0x78, 0xa4, 0xf4, 0xbc, 0x60, 0x77, - 0x89, 0x8e, 0xf9, 0x0a, 0x79, 0x15, 0x4f, 0xdf, 0xa0, 0xa3, 0xaf, 0x17, 0x28, 0xde, 0xc7, 0x8f, 0x9a, 0x07, 0xce, - 0x4c, 0x03, 0x09, 0x3e, 0xf0, 0xac, 0x17, 0x00, 0xe6, 0xe5, 0x6a, 0x7a, 0x04, 0x16, 0x78, 0x1a, 0xc2, 0xbf, 0x79, - 0xb1, 0xf8, 0xc1, 0xcd, 0x24, 0x2c, 0xdf, 0x0d, 0xe6, 0x80, 0xd2, 0xdc, 0x60, 0x5e, 0x31, 0xc7, 0x22, 0x5f, 0xe5, - 0x52, 0x69, 0xde, 0x55, 0x6e, 0x2a, 0x15, 0xbf, 0xbc, 0xbf, 0xa0, 0x7c, 0xac, 0x9a, 0x0a, 0xb7, 0x1c, 0x3a, 0xd6, - 0xe6, 0x9a, 0xdc, 0xe7, 0x83, 0x2f, 0x4f, 0x96, 0x2c, 0x71, 0x49, 0x0d, 0x04, 0xcc, 0x2f, 0x90, 0x03, 0x0a, 0xbf, - 0x68, 0x78, 0x4c, 0xa7, 0xc1, 0x94, 0xdd, 0x78, 0x13, 0xce, 0x97, 0x1a, 0x0a, 0xbf, 0xa7, 0x4c, 0xb4, 0xf8, 0x1c, - 0x38, 0x06, 0x39, 0x1c, 0x4c, 0x5c, 0x0c, 0x51, 0x3c, 0x08, 0x42, 0x75, 0xf8, 0x65, 0xe6, 0x9b, 0xd9, 0xb4, 0x08, - 0x90, 0x14, 0xfd, 0x32, 0x62, 0xfe, 0xbf, 0x07, 0x5f, 0xc2, 0xc5, 0xfd, 0xe5, 0x95, 0xaa, 0xf7, 0x13, 0x6b, 0x11, - 0xb1, 0xd9, 0xe0, 0xcb, 0x9a, 0xe4, 0xe0, 0xc8, 0xde, 0xd3, 0x58, 0xd4, 0x76, 0x2b, 0x0f, 0x15, 0xd7, 0xde, 0x8b, - 0xa9, 0x1f, 0x72, 0x6e, 0x1d, 0x38, 0xc0, 0x4d, 0x81, 0xc7, 0x76, 0xfa, 0xc8, 0x3f, 0x8f, 0x7d, 0x77, 0xf2, 0xa1, - 0x4f, 0x6f, 0x0a, 0x0f, 0x26, 0xdc, 0xd6, 0x13, 0x77, 0xd5, 0xc3, 0xeb, 0x55, 0x2e, 0x04, 0xd7, 0x6c, 0x2a, 0xcd, - 0x28, 0xbb, 0xda, 0xbd, 0x8c, 0x5b, 0x79, 0x83, 0x5f, 0xc6, 0x4f, 0xdd, 0x2e, 0xbc, 0x84, 0x89, 0x4f, 0xe1, 0x43, - 0x9a, 0x0a, 0x46, 0x9d, 0x58, 0x54, 0x64, 0xac, 0xad, 0xb6, 0xe2, 0x74, 0xbf, 0xed, 0xdc, 0x38, 0xf6, 0xa2, 0xe5, - 0x58, 0xdd, 0x9f, 0x9d, 0xee, 0xa2, 0x6d, 0x1d, 0xfb, 0x66, 0xdb, 0x3a, 0x86, 0x3f, 0x3f, 0x1f, 0x5b, 0xdd, 0x85, - 0xd9, 0xb2, 0x0e, 0x7f, 0x76, 0x5a, 0xbe, 0xd9, 0xb5, 0x8e, 0xe1, 0xcf, 0x39, 0xb5, 0x02, 0x06, 0x88, 0xf8, 0x9d, - 0x2f, 0x0b, 0x58, 0x40, 0xfa, 0x9d, 0xe9, 0x64, 0x8d, 0xc2, 0xf5, 0x56, 0xa3, 0xd7, 0x05, 0x94, 0x41, 0x29, 0x7b, - 0xd0, 0x14, 0xa1, 0xaf, 0x05, 0x03, 0x46, 0x49, 0x7a, 0x84, 0x79, 0x9b, 0xf0, 0x41, 0x17, 0x79, 0x51, 0x6a, 0x8f, - 0x11, 0x6f, 0x53, 0x9f, 0x0b, 0x44, 0x24, 0x70, 0x25, 0x45, 0xf0, 0x4f, 0x2b, 0x0c, 0x69, 0x27, 0xd2, 0x5e, 0x49, - 0x58, 0x29, 0x4f, 0x22, 0x9e, 0xee, 0x1e, 0x38, 0x7a, 0xe1, 0xb3, 0x2c, 0x89, 0xe5, 0x67, 0xed, 0x5b, 0xca, 0x2e, - 0xf6, 0x49, 0xfd, 0x60, 0x56, 0xa5, 0x3c, 0x21, 0x12, 0x44, 0x02, 0x9f, 0x7a, 0x51, 0x36, 0x3c, 0x09, 0x45, 0x3b, - 0xf5, 0x49, 0x54, 0x74, 0xc8, 0xf6, 0x77, 0x06, 0x54, 0xf2, 0x8d, 0xeb, 0x4b, 0x86, 0x6c, 0x52, 0xcb, 0x47, 0x19, - 0xe6, 0x7f, 0xfa, 0x34, 0x1f, 0x9c, 0x59, 0x1a, 0xf7, 0x89, 0xd3, 0x81, 0x6b, 0xb7, 0xc3, 0xda, 0x5b, 0x6d, 0x2a, - 0x77, 0xc7, 0x90, 0xcf, 0x83, 0x47, 0x0b, 0xbb, 0x29, 0x61, 0xb1, 0xd1, 0x68, 0xd8, 0x59, 0xb1, 0xd7, 0x80, 0xe8, - 0xfb, 0x25, 0x56, 0x47, 0xd5, 0xfb, 0x81, 0x30, 0x3f, 0x08, 0xb6, 0xc4, 0xcd, 0xe7, 0xbc, 0x98, 0x0a, 0xa0, 0xd9, - 0x32, 0x8f, 0x1d, 0x0e, 0xe2, 0x7f, 0xf6, 0x24, 0xd0, 0x59, 0x13, 0xec, 0x25, 0x4a, 0xa7, 0xb5, 0xe0, 0xbc, 0x97, - 0xdd, 0xab, 0x74, 0xa1, 0xb2, 0xf8, 0x54, 0x85, 0x22, 0x48, 0x03, 0x8b, 0x99, 0x9f, 0x33, 0x63, 0xd1, 0xec, 0xb6, - 0xc8, 0x0b, 0x0c, 0x0f, 0x93, 0x90, 0x08, 0xc7, 0x51, 0xfd, 0xe9, 0xd3, 0xc6, 0x4b, 0x88, 0x8c, 0x73, 0x62, 0x96, - 0x64, 0xb9, 0x29, 0x55, 0x19, 0xbf, 0xa9, 0x32, 0x8a, 0xc9, 0xfa, 0x45, 0xac, 0x21, 0x6c, 0x5c, 0x69, 0xef, 0xe1, - 0xcf, 0x31, 0x73, 0x13, 0x8b, 0x2b, 0x4b, 0x35, 0xe9, 0x72, 0x37, 0x1c, 0xd6, 0x06, 0xeb, 0x56, 0x1e, 0xf9, 0x92, - 0x47, 0x96, 0x7d, 0xb2, 0x79, 0xb9, 0xe6, 0x51, 0x1d, 0xa0, 0x8f, 0x8f, 0x76, 0x1e, 0x38, 0xec, 0x6d, 0xe2, 0x52, - 0x40, 0x17, 0xf9, 0xca, 0x0d, 0x13, 0x57, 0xa4, 0x5b, 0x04, 0xba, 0xbc, 0x5f, 0x6b, 0x7e, 0x21, 0x45, 0x7e, 0x18, - 0xbe, 0xbd, 0xf8, 0x5a, 0xe1, 0xfb, 0x9f, 0xac, 0x05, 0x90, 0x91, 0xa1, 0x94, 0x3c, 0x03, 0x4a, 0xc9, 0xa3, 0xf0, - 0x9c, 0x50, 0x90, 0xa7, 0x26, 0x3d, 0x20, 0x08, 0xa2, 0x00, 0x9a, 0x6c, 0x28, 0x96, 0x6b, 0x3f, 0xf1, 0x56, 0x6e, - 0x94, 0x1c, 0x60, 0x3e, 0x1e, 0x40, 0x72, 0x6a, 0x53, 0x3c, 0x08, 0x32, 0xc3, 0x10, 0x01, 0x57, 0x93, 0x40, 0xd8, - 0x61, 0xcc, 0x3c, 0x3f, 0x33, 0xc3, 0x10, 0x1f, 0x70, 0x27, 0x13, 0xb6, 0x4a, 0x06, 0x85, 0xbc, 0x3f, 0xe1, 0x24, - 0x61, 0x89, 0x19, 0x27, 0x11, 0x73, 0x97, 0x6a, 0x16, 0xd8, 0xbb, 0xda, 0x5f, 0xbc, 0x1e, 0x2f, 0xbd, 0x24, 0x8b, - 0x8c, 0x4b, 0x13, 0x04, 0x83, 0x08, 0x18, 0xe2, 0x70, 0x94, 0x72, 0x10, 0x9e, 0x87, 0xf3, 0xd2, 0x8e, 0xca, 0x29, - 0x97, 0x53, 0x8c, 0xbb, 0x4e, 0x9c, 0x0c, 0x48, 0x8b, 0x27, 0xa1, 0x7f, 0xcd, 0x63, 0x58, 0x64, 0x01, 0x7c, 0xd5, - 0xe1, 0x09, 0x67, 0x6f, 0x15, 0x0c, 0xbb, 0xa2, 0x76, 0x6c, 0x88, 0x2c, 0xdf, 0x14, 0xdd, 0xe2, 0x80, 0x57, 0x86, - 0xab, 0x89, 0x7a, 0xc6, 0xe4, 0x20, 0x34, 0x96, 0x0b, 0x20, 0x84, 0x0a, 0x06, 0x33, 0x0b, 0x67, 0x98, 0xb9, 0x53, - 0xe2, 0xa8, 0x90, 0x56, 0xfa, 0xf8, 0xf1, 0xd5, 0xe8, 0xb7, 0xff, 0x40, 0x06, 0x93, 0x85, 0x23, 0x62, 0x4a, 0x5c, - 0xca, 0xb5, 0x38, 0xf5, 0x69, 0x8c, 0xd0, 0x58, 0x8a, 0x4d, 0x45, 0x68, 0x1d, 0xb1, 0xb5, 0xd2, 0xd1, 0x95, 0x08, - 0xad, 0x08, 0xc9, 0x8d, 0x74, 0x11, 0xf9, 0x62, 0x04, 0xcb, 0x3b, 0x12, 0x01, 0x57, 0x94, 0x5f, 0xee, 0x5e, 0x1e, - 0x2b, 0x79, 0xec, 0xa1, 0x3a, 0x8b, 0x1e, 0xda, 0x43, 0xc3, 0x13, 0x57, 0x41, 0xa2, 0x05, 0xc9, 0x8f, 0xb8, 0x77, - 0x00, 0xd3, 0x5c, 0x84, 0x4b, 0x66, 0x79, 0xe1, 0xc1, 0x2d, 0x1b, 0x9b, 0xee, 0xca, 0x23, 0xbb, 0x1c, 0x94, 0xbb, - 0x29, 0x44, 0xf9, 0x65, 0xe6, 0x2e, 0x44, 0x5f, 0xa7, 0x39, 0x28, 0xc3, 0x62, 0x2c, 0xcd, 0x4e, 0x2b, 0xd7, 0x03, - 0x42, 0xfc, 0x02, 0x09, 0x8e, 0xe1, 0xf0, 0xe4, 0xc0, 0x1d, 0x16, 0x83, 0xf9, 0x5a, 0x22, 0xeb, 0x4c, 0xf1, 0x12, - 0x38, 0xa5, 0x98, 0xbc, 0x22, 0xfc, 0x6e, 0xfe, 0x60, 0x86, 0xb3, 0x99, 0x1c, 0x80, 0xd7, 0x2a, 0x0e, 0x2f, 0x03, - 0x5a, 0xbe, 0xa5, 0xc3, 0x15, 0x7d, 0xa9, 0xfa, 0x89, 0xec, 0xa7, 0xda, 0xc3, 0xc8, 0xdb, 0x30, 0x67, 0x38, 0xee, - 0x95, 0x40, 0xbe, 0x19, 0xc4, 0x1e, 0x53, 0x25, 0x8e, 0x47, 0xca, 0x69, 0x23, 0x1a, 0x28, 0x97, 0x47, 0x83, 0x01, - 0xa1, 0xb9, 0x32, 0xb6, 0x03, 0x20, 0xd6, 0x64, 0xe0, 0x81, 0xc9, 0x26, 0xd0, 0xd0, 0x24, 0x77, 0x59, 0x6c, 0x54, - 0x9e, 0x4e, 0x75, 0x8c, 0x07, 0xae, 0xd8, 0x7e, 0x85, 0x0d, 0x0a, 0x1b, 0x8f, 0xaf, 0x3b, 0xe0, 0x77, 0xd1, 0x4f, - 0x09, 0xcd, 0x2b, 0x5f, 0x11, 0x46, 0x37, 0x7d, 0xf7, 0x3e, 0x94, 0xcc, 0x98, 0x78, 0x44, 0x93, 0x73, 0x2c, 0xbd, - 0x10, 0x9e, 0xc4, 0x95, 0x83, 0x96, 0x25, 0x32, 0xa9, 0x1e, 0x36, 0x39, 0xf9, 0xc8, 0xae, 0xb3, 0x26, 0xd7, 0x2d, - 0x4e, 0x06, 0x91, 0x67, 0x9a, 0x9f, 0xc3, 0xc2, 0x4b, 0x44, 0x0b, 0xe9, 0xc9, 0x01, 0xcc, 0x0f, 0xa2, 0xb0, 0x14, - 0x08, 0x27, 0x4f, 0x87, 0x50, 0x2f, 0x6e, 0x4c, 0xa6, 0x58, 0x67, 0x53, 0x41, 0xf3, 0x21, 0x63, 0x29, 0x65, 0xe5, - 0x93, 0xaa, 0x54, 0x69, 0x19, 0xbb, 0x9e, 0x08, 0xdc, 0x9d, 0xf5, 0xe7, 0x87, 0x35, 0xa6, 0xfc, 0x49, 0xfb, 0x09, - 0x13, 0x41, 0x0e, 0xce, 0x93, 0x86, 0x38, 0x08, 0x4d, 0x55, 0x88, 0x9e, 0xdd, 0x52, 0x21, 0xdf, 0xc7, 0xdb, 0x6a, - 0xe5, 0x94, 0x53, 0x56, 0x6d, 0xe5, 0x6a, 0xea, 0x63, 0xdc, 0xf1, 0x95, 0xda, 0x58, 0x0a, 0xf5, 0xce, 0x93, 0x01, - 0x54, 0x15, 0xb2, 0x78, 0x77, 0xb5, 0xa2, 0xca, 0x7a, 0xff, 0xe4, 0x80, 0xd8, 0xd2, 0x21, 0xed, 0xb0, 0xe1, 0x09, - 0x98, 0x72, 0xd3, 0xa2, 0xbb, 0xab, 0x15, 0x5f, 0x52, 0xfa, 0x45, 0x6f, 0x0e, 0x16, 0xc9, 0xd2, 0x1f, 0xfe, 0x1f, - 0x59, 0xea, 0xe1, 0x3d, 0xe8, 0x69, 0x03, 0x00}; + 0xdb, 0xdb, 0xdb, 0xff, 0xd3, 0xdc, 0xb3, 0x6e, 0xb7, 0x6d, 0x23, 0xfd, 0xbf, 0x4f, 0xc1, 0x30, 0xd9, 0x94, 0x4c, + 0x48, 0x9a, 0x94, 0x2c, 0x5b, 0x91, 0x2c, 0xb9, 0xcd, 0xa5, 0x5b, 0x77, 0xdd, 0xa6, 0x27, 0x71, 0xfb, 0xed, 0xae, + 0xeb, 0x63, 0x51, 0x12, 0x24, 0x71, 0x43, 0x91, 0x3a, 0x24, 0xe5, 0x4b, 0x15, 0xee, 0xb3, 0xec, 0x23, 0x7c, 0xcf, + 0xd0, 0x27, 0xfb, 0xce, 0xcc, 0x00, 0x24, 0x78, 0x93, 0xe4, 0x4d, 0xda, 0x7e, 0xa7, 0x4d, 0x22, 0x82, 0x00, 0x08, + 0x0c, 0x80, 0xb9, 0x61, 0x2e, 0x26, 0x98, 0x36, 0x9a, 0xeb, 0xc8, 0x67, 0xc1, 0x24, 0x84, 0xc4, 0x24, 0xa9, 0x40, + 0xf8, 0xac, 0x84, 0xf0, 0x21, 0x2e, 0x2a, 0x4f, 0xac, 0xf1, 0x7e, 0x11, 0xde, 0x7e, 0xed, 0xfb, 0xb2, 0xfc, 0x2e, + 0x98, 0x3e, 0x2e, 0xd2, 0x16, 0x10, 0x88, 0x06, 0xd7, 0x10, 0x96, 0x17, 0x5f, 0xf3, 0x8b, 0xe3, 0xe9, 0xf5, 0xf8, + 0xfe, 0x9a, 0x2b, 0xa7, 0xb3, 0xc0, 0xb4, 0xaf, 0x46, 0x27, 0x53, 0xef, 0x46, 0x41, 0xce, 0x74, 0xa0, 0x82, 0x57, + 0x8f, 0xcf, 0xc6, 0xeb, 0x24, 0x09, 0x03, 0x33, 0x0a, 0x6f, 0xd5, 0xe1, 0x09, 0x3d, 0x88, 0x0a, 0x2e, 0x3d, 0xaa, + 0xca, 0x57, 0x13, 0xdf, 0x9b, 0x7c, 0x18, 0xa8, 0x4f, 0x36, 0xde, 0x60, 0x58, 0xe2, 0x3f, 0xed, 0x54, 0x1d, 0xc2, + 0x58, 0x95, 0xaf, 0x7d, 0xff, 0xe4, 0x80, 0x5a, 0x0c, 0x4f, 0x0e, 0xa6, 0xde, 0xcd, 0x50, 0xca, 0x11, 0xc2, 0x2f, + 0xd0, 0x06, 0x3c, 0x16, 0x63, 0x66, 0x72, 0x14, 0xa3, 0x73, 0xff, 0x84, 0x69, 0xb9, 0x14, 0x04, 0x41, 0x47, 0x68, + 0xbc, 0xda, 0x04, 0xf5, 0xaa, 0x3e, 0xf0, 0xfc, 0x1f, 0x3f, 0x6a, 0x99, 0x41, 0xe2, 0x42, 0x8a, 0xd6, 0x85, 0xf7, + 0x3d, 0x58, 0xc5, 0xc0, 0x90, 0x23, 0xba, 0x26, 0x62, 0x8a, 0xf9, 0xba, 0x31, 0x49, 0x0d, 0x4c, 0xb5, 0xe2, 0xae, + 0x80, 0x47, 0xe0, 0x3f, 0x25, 0xd1, 0x68, 0x02, 0xe9, 0x95, 0x25, 0x04, 0xaf, 0x4b, 0xca, 0x77, 0x3a, 0x9b, 0x3c, + 0x60, 0x1c, 0x68, 0xcd, 0xf1, 0x3b, 0xa4, 0x10, 0xd7, 0x7c, 0x1d, 0xd2, 0x7b, 0x65, 0x51, 0x5a, 0xdc, 0x54, 0x24, + 0xd4, 0x12, 0x70, 0x39, 0x2d, 0xac, 0x50, 0xaf, 0xbc, 0x5e, 0x22, 0x7c, 0xe0, 0xa3, 0xb8, 0x69, 0xc9, 0xe0, 0x32, + 0x47, 0x4b, 0x8c, 0x12, 0x3d, 0x06, 0xf7, 0x2e, 0xe9, 0x06, 0x81, 0x19, 0xda, 0x65, 0x6c, 0x84, 0x57, 0x39, 0x0d, + 0x8b, 0x09, 0x7d, 0xf6, 0xc2, 0x34, 0x8f, 0xe4, 0x4b, 0xab, 0x3e, 0x7c, 0xb2, 0x09, 0x90, 0xe8, 0xc5, 0x83, 0x61, + 0x71, 0x1f, 0x24, 0xee, 0xd8, 0xa4, 0xcd, 0xac, 0x2a, 0x5f, 0x4d, 0xc7, 0x7e, 0xb6, 0xd8, 0x74, 0x34, 0x16, 0x6e, + 0x30, 0xf5, 0xd9, 0x85, 0x3b, 0xfe, 0x16, 0xeb, 0xbc, 0x1e, 0xfb, 0xaf, 0xa0, 0x42, 0xaa, 0x0e, 0x9f, 0x6c, 0x88, + 0xac, 0xd7, 0xa1, 0xf1, 0x94, 0xb6, 0x40, 0xf9, 0x3b, 0x3c, 0xf7, 0x0e, 0x8b, 0xa8, 0x35, 0x0e, 0x96, 0x48, 0x31, + 0xe1, 0xd9, 0xe2, 0xc8, 0x78, 0xee, 0x17, 0xd8, 0x9b, 0x0a, 0x3f, 0x94, 0x30, 0xae, 0x50, 0x1c, 0x50, 0x79, 0x67, + 0xca, 0x83, 0x25, 0x92, 0xfb, 0x2e, 0xbc, 0x15, 0x23, 0xe5, 0x00, 0xa0, 0x58, 0x85, 0xa7, 0xaf, 0x46, 0x27, 0xf2, + 0xfd, 0x00, 0x2a, 0x51, 0xa9, 0x5f, 0xf8, 0x95, 0xaa, 0x4a, 0x9e, 0x09, 0x68, 0x75, 0xa7, 0x0e, 0x4f, 0x0e, 0xe4, + 0xda, 0xc3, 0x51, 0xef, 0x5c, 0x9a, 0x1c, 0xf6, 0x0a, 0x40, 0x28, 0x96, 0x55, 0xa8, 0x0e, 0x24, 0xc7, 0xcb, 0xe9, + 0x12, 0x6d, 0x0f, 0x81, 0x16, 0x43, 0xbd, 0x97, 0xad, 0x11, 0xd9, 0xe0, 0x89, 0xde, 0x46, 0xfc, 0xdf, 0x7c, 0xce, + 0xa8, 0xd3, 0x64, 0x41, 0x1c, 0x46, 0x2a, 0xcc, 0xa3, 0x9c, 0x21, 0x47, 0x91, 0x32, 0x73, 0xe1, 0x8c, 0x6a, 0xa9, + 0x29, 0x40, 0xe4, 0xa0, 0xdc, 0x54, 0x9a, 0xd8, 0x48, 0xcf, 0x7f, 0x28, 0x7c, 0x32, 0x25, 0xac, 0x94, 0x0d, 0xb0, + 0x39, 0xf3, 0xd0, 0xe5, 0x5b, 0xcf, 0xf8, 0x9f, 0xd0, 0x98, 0xbb, 0xc6, 0xd2, 0x35, 0xde, 0x07, 0x57, 0x69, 0xed, + 0xea, 0x64, 0x59, 0xc3, 0x0c, 0xd6, 0xd7, 0x20, 0xd6, 0x0e, 0xd7, 0x80, 0x70, 0xbd, 0x80, 0x67, 0x71, 0xeb, 0x80, + 0x0b, 0x37, 0x9a, 0x33, 0x91, 0xac, 0x4b, 0xbc, 0x4d, 0x38, 0x54, 0x74, 0x09, 0x2c, 0x10, 0x88, 0x4a, 0x08, 0x38, + 0x9e, 0x35, 0x49, 0x22, 0xff, 0x6f, 0xec, 0x1e, 0x24, 0xcf, 0x38, 0x09, 0x57, 0xa0, 0x9d, 0x70, 0xe7, 0x5c, 0xdb, + 0x6c, 0x00, 0x2f, 0xb3, 0xcf, 0xe7, 0x3e, 0x7e, 0x64, 0x52, 0xfe, 0xa8, 0x24, 0x9c, 0xcf, 0x7d, 0xa6, 0x49, 0x79, + 0xa6, 0xb2, 0xcf, 0x9c, 0x3e, 0xb2, 0x45, 0x8c, 0x62, 0x3d, 0x6d, 0x3a, 0x39, 0x39, 0x2b, 0x28, 0xee, 0x75, 0x49, + 0x58, 0xc7, 0xdb, 0xa8, 0x1b, 0xbc, 0xd0, 0xe5, 0xeb, 0x92, 0x9f, 0x4c, 0x73, 0x1a, 0xae, 0xc7, 0x3e, 0x33, 0x71, + 0xbb, 0xc3, 0x27, 0x37, 0xe3, 0xf5, 0x78, 0xec, 0x53, 0x62, 0x28, 0x88, 0xb4, 0x15, 0xc6, 0xa8, 0x01, 0x4b, 0xf5, + 0x3e, 0x32, 0x68, 0x49, 0x79, 0xf8, 0x60, 0x1d, 0x07, 0x62, 0x03, 0x7d, 0x20, 0x01, 0x6d, 0x57, 0xf5, 0xd0, 0x0e, + 0x54, 0x10, 0x57, 0x58, 0xac, 0xf6, 0x6b, 0x38, 0xb9, 0xc1, 0xa5, 0xfa, 0x1e, 0x21, 0x8c, 0xd9, 0xeb, 0x5f, 0xd1, + 0xde, 0x55, 0x0d, 0x95, 0x8c, 0x7c, 0x78, 0x1e, 0x31, 0xd5, 0x50, 0x5f, 0x7b, 0xee, 0x3c, 0x08, 0xe3, 0xc4, 0x9b, + 0xa8, 0x57, 0xfd, 0x33, 0x4f, 0xbb, 0x5c, 0x26, 0x9a, 0x7e, 0x65, 0xfc, 0x55, 0xce, 0xf8, 0x24, 0x50, 0x21, 0x26, + 0x7c, 0x6a, 0xa8, 0x23, 0x9f, 0x9e, 0x6d, 0xf5, 0x04, 0xca, 0xc5, 0x3a, 0x7f, 0x1d, 0x40, 0xad, 0x52, 0xee, 0x28, + 0x4c, 0x0a, 0x08, 0xb9, 0xa3, 0xfe, 0xaa, 0xf7, 0x49, 0x2b, 0xf3, 0x6a, 0xbd, 0x41, 0x5e, 0x21, 0xc9, 0xa9, 0x2b, + 0x86, 0x3b, 0x17, 0x3e, 0x82, 0xf4, 0xfc, 0x48, 0xb6, 0x6f, 0x2f, 0xd0, 0xe9, 0xd1, 0xd7, 0x45, 0xc6, 0x03, 0x18, + 0x04, 0x30, 0x2e, 0x0b, 0xc2, 0x44, 0x81, 0x18, 0x5e, 0xf0, 0xc1, 0x51, 0xd9, 0x1e, 0x96, 0xf7, 0xaa, 0xe9, 0x29, + 0xc7, 0x02, 0x2f, 0x91, 0x58, 0x8a, 0xec, 0xef, 0x18, 0x8e, 0xb2, 0x10, 0xb1, 0x87, 0x7b, 0x61, 0xc1, 0xf2, 0x15, + 0xd8, 0x36, 0x09, 0xb1, 0x17, 0x09, 0xf6, 0x93, 0x4d, 0x7c, 0x2a, 0xa8, 0xf6, 0x59, 0x8c, 0x6b, 0x09, 0xfc, 0x08, + 0x27, 0xe3, 0xa9, 0xaa, 0x9c, 0x0a, 0x52, 0x83, 0x75, 0x0b, 0xf8, 0x53, 0x13, 0x5c, 0xae, 0x48, 0xea, 0xae, 0xf1, + 0x14, 0xd4, 0x82, 0xef, 0x2a, 0x1d, 0x3d, 0x08, 0xcb, 0x93, 0xb1, 0x54, 0x09, 0xd8, 0xd6, 0x22, 0x45, 0x00, 0xcc, + 0xc5, 0x99, 0x80, 0x51, 0x7a, 0x0d, 0xfc, 0x23, 0xc4, 0xaa, 0x12, 0x73, 0x34, 0x42, 0x39, 0x5d, 0x98, 0x17, 0xac, + 0xd6, 0x09, 0xc6, 0x20, 0x87, 0x01, 0xb0, 0x54, 0x55, 0x50, 0x5a, 0x04, 0x64, 0x9e, 0x4b, 0x41, 0xa9, 0xaa, 0x78, + 0xd3, 0x6a, 0x19, 0x57, 0xdd, 0x00, 0x8e, 0xc3, 0x69, 0xa0, 0x06, 0x1f, 0x1e, 0x23, 0x3e, 0x8d, 0x89, 0x91, 0x27, + 0xf0, 0xd0, 0x26, 0x78, 0xd3, 0x5d, 0x83, 0x40, 0x26, 0xd4, 0x4f, 0x5f, 0xf3, 0x6b, 0x27, 0x0b, 0x71, 0x89, 0x0b, + 0xd3, 0x1c, 0x3d, 0xd9, 0x04, 0xe9, 0x29, 0xc0, 0x6e, 0xf0, 0x64, 0xe3, 0x66, 0x46, 0x54, 0xea, 0x85, 0x4a, 0x16, + 0x54, 0x23, 0x04, 0xc3, 0x28, 0xbd, 0xce, 0x5d, 0x1a, 0xf3, 0xf9, 0xc2, 0x96, 0xa4, 0x72, 0x05, 0x6d, 0x9a, 0x06, + 0xdc, 0x72, 0x69, 0x15, 0x79, 0x4b, 0x37, 0xba, 0x27, 0x43, 0x27, 0x43, 0xb6, 0x86, 0xd2, 0x55, 0x85, 0xe8, 0x01, + 0x01, 0x80, 0x48, 0x83, 0xaa, 0x7c, 0x95, 0x95, 0x31, 0x3e, 0xdb, 0xcc, 0xda, 0x03, 0xbe, 0x75, 0xad, 0x3e, 0x67, + 0x16, 0xa9, 0x34, 0xa8, 0x49, 0x5f, 0x8b, 0x1b, 0xa6, 0x17, 0x17, 0xa7, 0x17, 0x14, 0x37, 0x1a, 0x4e, 0x86, 0x28, + 0x05, 0x8d, 0x1b, 0x67, 0x86, 0xe9, 0x0e, 0xeb, 0x57, 0x94, 0xde, 0xfd, 0xa1, 0xcb, 0xc1, 0x60, 0x39, 0x02, 0x58, + 0x0e, 0xe2, 0xae, 0x7f, 0x7a, 0x77, 0x96, 0xe5, 0x57, 0x04, 0xd5, 0xf8, 0x88, 0x6f, 0xcc, 0x18, 0xd9, 0x8c, 0x08, + 0x59, 0x0c, 0xca, 0x84, 0xa8, 0x64, 0x5b, 0x28, 0x82, 0xa3, 0x41, 0x63, 0xa7, 0xa3, 0x11, 0x0d, 0x06, 0x21, 0xb6, + 0x8a, 0xd2, 0x93, 0x03, 0xaa, 0x4d, 0x44, 0x91, 0x2a, 0x01, 0x18, 0x22, 0x98, 0x61, 0x0e, 0x05, 0x48, 0x05, 0x3d, + 0x70, 0x72, 0xf9, 0xc6, 0x5a, 0xe2, 0x05, 0xa4, 0x73, 0x5a, 0xe4, 0x68, 0xb0, 0x95, 0x3a, 0x3c, 0xc1, 0xe4, 0x8e, + 0x40, 0xd6, 0x21, 0xfc, 0xd1, 0xc9, 0x01, 0x3d, 0x2a, 0xa5, 0x13, 0x91, 0x77, 0x22, 0x14, 0x94, 0x3d, 0xde, 0xc1, + 0x83, 0x8e, 0x4a, 0x9c, 0xb0, 0x15, 0x94, 0xba, 0xa9, 0xaa, 0x2c, 0x39, 0x07, 0xc5, 0xe3, 0xac, 0x41, 0x10, 0x16, + 0x1b, 0x8c, 0xdf, 0x55, 0x65, 0xe9, 0xde, 0xe1, 0xcc, 0xc5, 0x1b, 0xf7, 0x4e, 0x73, 0xf8, 0xab, 0xfc, 0xac, 0xc5, + 0xc5, 0xb3, 0x36, 0xe1, 0x8b, 0x0b, 0x1e, 0x56, 0x82, 0x73, 0xd6, 0x16, 0x68, 0xb9, 0x52, 0xb3, 0xb8, 0x0b, 0xb1, + 0xb8, 0xd3, 0x86, 0xc5, 0x9d, 0x6e, 0x59, 0x5c, 0x9f, 0x2f, 0xa4, 0x92, 0x81, 0x2e, 0x42, 0xaf, 0xd9, 0x0c, 0x78, + 0x9c, 0x1f, 0xe9, 0xf1, 0x73, 0x86, 0x70, 0x32, 0x63, 0x1f, 0xac, 0x46, 0x1b, 0x60, 0x55, 0x07, 0x17, 0x09, 0x10, + 0xd5, 0x89, 0x67, 0xa7, 0x6e, 0x22, 0x29, 0x04, 0x34, 0xbf, 0x3c, 0x5f, 0xd8, 0xa5, 0xd8, 0xd0, 0xd0, 0x16, 0x0d, + 0x33, 0x5d, 0x6c, 0x99, 0xe9, 0xa4, 0x70, 0x74, 0xf9, 0xb4, 0xe9, 0x10, 0xca, 0x93, 0x82, 0x3d, 0x08, 0x96, 0xf4, + 0xb8, 0x65, 0x8a, 0xfb, 0xb0, 0x19, 0xc7, 0x4a, 0x3b, 0x6a, 0xe5, 0xc6, 0xf1, 0x6d, 0x18, 0xc1, 0x55, 0x34, 0x74, + 0xf3, 0xb0, 0x2d, 0xb5, 0xf4, 0x02, 0x1e, 0xe5, 0xaa, 0x71, 0x33, 0xe5, 0xef, 0xe5, 0x2d, 0xd5, 0xea, 0x74, 0xa8, + 0xc6, 0xca, 0x4d, 0x12, 0x16, 0x21, 0xd0, 0x5d, 0x48, 0x87, 0xf0, 0xff, 0x64, 0x9b, 0xd5, 0xe0, 0x10, 0x5f, 0xc2, + 0xea, 0x88, 0xa1, 0x57, 0xc0, 0x82, 0xd1, 0xdd, 0x53, 0xa0, 0x6f, 0xa4, 0x88, 0x99, 0x51, 0x06, 0xf8, 0x1f, 0xf0, + 0xb8, 0x6a, 0x91, 0xe4, 0xd3, 0xe9, 0x1c, 0xe9, 0xd6, 0xca, 0x9d, 0xbe, 0x07, 0x8b, 0x07, 0xad, 0x65, 0x80, 0xf7, + 0x82, 0x1c, 0x1f, 0x33, 0x22, 0x9e, 0x70, 0x92, 0x23, 0x49, 0xc4, 0x92, 0xdc, 0x36, 0x14, 0xdc, 0xca, 0x5d, 0x73, + 0x76, 0xb5, 0x69, 0xa5, 0x07, 0x73, 0x4f, 0xaf, 0x60, 0x4d, 0x40, 0x6d, 0xfe, 0x60, 0x98, 0xe9, 0xda, 0x7c, 0xc3, + 0x39, 0xd2, 0xe1, 0x4a, 0xec, 0x12, 0x12, 0x5f, 0xdb, 0x42, 0x5a, 0x1e, 0x45, 0x40, 0xb5, 0x2e, 0xed, 0xab, 0xf4, + 0xe9, 0x1c, 0x7f, 0x39, 0x57, 0xe9, 0xd3, 0x31, 0xfe, 0x6a, 0x5d, 0x61, 0x4a, 0xcf, 0x1a, 0x35, 0x81, 0x34, 0x67, + 0x75, 0x58, 0xd8, 0x4f, 0x64, 0x98, 0xfb, 0x80, 0x6d, 0xc3, 0x17, 0xf8, 0xf1, 0x93, 0x4d, 0x0c, 0xae, 0xe8, 0xf2, + 0x1c, 0x02, 0x2b, 0xd2, 0xd3, 0xda, 0xf2, 0x79, 0x43, 0xf9, 0x58, 0xff, 0x83, 0x09, 0x3f, 0xee, 0x92, 0x30, 0xa7, + 0x29, 0x45, 0x25, 0xc7, 0xf5, 0xd8, 0x0b, 0xdc, 0xe8, 0xfe, 0x9a, 0xa4, 0x10, 0x4d, 0xd2, 0xf6, 0x3e, 0xca, 0xa5, + 0xff, 0xfb, 0xa2, 0x1d, 0x40, 0x22, 0xdd, 0x65, 0xdd, 0x73, 0x42, 0x3f, 0xf8, 0x7b, 0x24, 0xf1, 0x77, 0x05, 0x39, + 0x95, 0x2f, 0x48, 0xe1, 0x43, 0xd7, 0x4f, 0x36, 0x1a, 0xab, 0x76, 0x53, 0x9a, 0x6d, 0x89, 0x81, 0x84, 0xe5, 0x41, + 0x99, 0x77, 0x39, 0xf5, 0x7a, 0x78, 0xd1, 0x3f, 0x0e, 0xef, 0xcc, 0x27, 0x9b, 0xe4, 0x54, 0x5d, 0xba, 0xd1, 0x07, + 0x36, 0x35, 0x27, 0x5e, 0x34, 0xf1, 0x81, 0x79, 0x1c, 0xfb, 0x6e, 0xf0, 0x81, 0x3f, 0x9a, 0xe1, 0x3a, 0x41, 0xd3, + 0x9d, 0x9d, 0x22, 0xb2, 0x80, 0x09, 0xe9, 0x0f, 0x91, 0xab, 0xad, 0x81, 0x82, 0xf2, 0x2a, 0xd3, 0xbf, 0xe5, 0x8c, + 0x62, 0x5e, 0xcb, 0x00, 0xcb, 0x73, 0xb0, 0x26, 0x02, 0x57, 0x7e, 0x43, 0xc5, 0xf5, 0x52, 0x0d, 0x79, 0xaa, 0x74, + 0xe5, 0x96, 0xe5, 0xa2, 0xbd, 0xc6, 0x1e, 0xfe, 0xfb, 0xcf, 0x41, 0xc9, 0x43, 0x3e, 0x97, 0xf5, 0xf2, 0x69, 0x33, + 0x84, 0x52, 0x93, 0x5c, 0xc8, 0x1e, 0xf0, 0x71, 0xce, 0x60, 0x36, 0x7f, 0x5a, 0x6e, 0xec, 0xc6, 0xf1, 0x7a, 0xc9, + 0xa6, 0x74, 0xb5, 0x76, 0x9a, 0x0f, 0xaa, 0x28, 0x87, 0xc8, 0x03, 0xfb, 0x65, 0xdd, 0x3a, 0x3e, 0x7c, 0x05, 0xa6, + 0x5c, 0xc0, 0x50, 0x86, 0xb3, 0x99, 0x9a, 0xab, 0x02, 0x76, 0x34, 0x73, 0x0e, 0x7f, 0x59, 0x7f, 0xf3, 0xc6, 0xfe, + 0x26, 0x6b, 0x1c, 0x00, 0x63, 0x2c, 0xec, 0x52, 0x38, 0x5f, 0x2c, 0x8d, 0x57, 0xcc, 0x68, 0xe6, 0x06, 0xcd, 0xd3, + 0xb9, 0x2c, 0x6c, 0xf1, 0x15, 0x63, 0x53, 0x60, 0xb8, 0x8d, 0x4a, 0xe9, 0xb5, 0xcf, 0x6e, 0x58, 0x66, 0xf3, 0x52, + 0xfd, 0x58, 0x4d, 0x0b, 0x0c, 0xca, 0xc9, 0x6f, 0x32, 0x39, 0x57, 0x27, 0x4d, 0x69, 0x84, 0x73, 0xe0, 0x33, 0x97, + 0x8f, 0x58, 0xe9, 0x48, 0x8d, 0x0c, 0x55, 0x1a, 0x40, 0xe3, 0xc8, 0x4e, 0x1b, 0xca, 0x7b, 0x80, 0xa8, 0x1b, 0xc6, + 0x66, 0x38, 0x7a, 0x0f, 0x92, 0x18, 0x70, 0x38, 0xf9, 0x70, 0xf2, 0xb4, 0x5c, 0x6b, 0xd2, 0x04, 0xb1, 0x3a, 0x5d, + 0x9a, 0x4a, 0x4a, 0x1a, 0x61, 0x06, 0x8e, 0xfe, 0x10, 0x42, 0x5d, 0x55, 0xbb, 0x36, 0x4a, 0x71, 0xe6, 0x63, 0x4c, + 0xf1, 0x1d, 0xb0, 0x38, 0x6e, 0x04, 0x58, 0xb6, 0xe8, 0x86, 0x9a, 0xd7, 0x2e, 0xc2, 0x23, 0x2f, 0x37, 0x6c, 0x03, + 0x58, 0x02, 0x9c, 0x60, 0xf9, 0x5b, 0x48, 0x5e, 0xae, 0x97, 0xdc, 0x90, 0x2f, 0x9a, 0x8f, 0x55, 0x6e, 0x64, 0xd5, + 0xf4, 0xfe, 0x56, 0xe5, 0x83, 0x2a, 0x90, 0xe9, 0xda, 0xa1, 0x69, 0x05, 0xd4, 0x5b, 0xd1, 0x2a, 0x61, 0x07, 0x62, + 0x4c, 0x25, 0xfc, 0xca, 0x66, 0x33, 0x36, 0x49, 0x62, 0x5d, 0xe8, 0x98, 0xb2, 0xb0, 0xda, 0x70, 0x7b, 0xf7, 0x68, + 0xa0, 0xfe, 0x00, 0xc1, 0x45, 0x44, 0xf4, 0x39, 0x3e, 0x20, 0x21, 0x33, 0xd5, 0x83, 0x89, 0x7a, 0x2c, 0x82, 0x88, + 0x7f, 0x05, 0xd4, 0xcc, 0x35, 0xe5, 0x38, 0x34, 0x4e, 0x7f, 0xf2, 0x7d, 0x11, 0x66, 0xe6, 0x7e, 0xdb, 0x51, 0xd1, + 0xb6, 0xe3, 0xbb, 0x71, 0xbe, 0xe9, 0x38, 0x76, 0xaa, 0x1a, 0xe0, 0xd4, 0xfa, 0xa1, 0xb4, 0x8d, 0x89, 0x40, 0x0d, + 0xd4, 0xf3, 0xb7, 0xaf, 0xfe, 0xf6, 0xe6, 0xf5, 0xbe, 0x18, 0x01, 0xbb, 0x6c, 0x43, 0x97, 0xeb, 0x60, 0x4b, 0xa7, + 0x3f, 0xfd, 0xf0, 0xb0, 0x6e, 0x5b, 0xce, 0x0b, 0x47, 0x35, 0xc8, 0x0e, 0x59, 0xc2, 0x8b, 0x93, 0xf0, 0x86, 0x45, + 0x9f, 0x0c, 0x06, 0xb9, 0xf3, 0xfa, 0xe1, 0xbe, 0xfd, 0xf1, 0xcd, 0x0f, 0x7b, 0x0f, 0xf5, 0xc8, 0xb1, 0x01, 0xb7, + 0x27, 0xe1, 0xea, 0x01, 0xb3, 0x6b, 0xab, 0x86, 0x3a, 0xf1, 0xc3, 0x98, 0x35, 0x8c, 0xe0, 0xd5, 0xf9, 0xdb, 0xf7, + 0x08, 0xae, 0x9c, 0x05, 0xa1, 0xae, 0x3e, 0x6d, 0xf2, 0x3f, 0xbe, 0x7b, 0xf3, 0xfe, 0xbd, 0x6a, 0x60, 0x5a, 0xe6, + 0x58, 0xee, 0x9d, 0x6f, 0xe2, 0x1d, 0x14, 0xa7, 0x76, 0xaf, 0x13, 0x55, 0x23, 0x41, 0xba, 0x38, 0x1b, 0x2a, 0xab, + 0x6c, 0x73, 0x4e, 0xed, 0xf8, 0x97, 0x49, 0xfa, 0xdd, 0x6b, 0x5e, 0x35, 0xf8, 0x68, 0x3b, 0x49, 0x2d, 0x94, 0x2c, + 0xbd, 0xe0, 0xba, 0xa6, 0xd4, 0xbd, 0xab, 0x29, 0x05, 0xf1, 0xb1, 0x82, 0x1f, 0xd7, 0xe1, 0x52, 0x62, 0x47, 0xd8, + 0xdd, 0x6e, 0x70, 0x49, 0x32, 0xdc, 0x27, 0x0c, 0x9a, 0xa7, 0xd5, 0x28, 0x8f, 0xba, 0xa6, 0x98, 0x0b, 0x5e, 0x19, + 0x6c, 0x27, 0x3e, 0x58, 0x5f, 0x33, 0xf9, 0x9e, 0xb1, 0xc8, 0xaa, 0x72, 0xdf, 0x89, 0x41, 0x49, 0x2a, 0xa0, 0x66, + 0x74, 0x37, 0xc3, 0x69, 0xca, 0xca, 0x9d, 0x82, 0x49, 0xb3, 0x39, 0x0e, 0x93, 0x24, 0x5c, 0xf6, 0x1c, 0x7b, 0x75, + 0xa7, 0x2a, 0x7d, 0xa1, 0xec, 0xe0, 0x16, 0xd7, 0xbd, 0xdf, 0xfe, 0x53, 0x42, 0xf3, 0x54, 0x7e, 0x9d, 0xb0, 0xe5, + 0x8a, 0x45, 0x6e, 0xb2, 0x8e, 0x58, 0xaa, 0xfc, 0xf6, 0xbf, 0xaf, 0x4a, 0x82, 0x7d, 0x5f, 0x6e, 0x43, 0x2c, 0xbd, + 0xdc, 0xe4, 0xda, 0x0f, 0x6f, 0x1f, 0xe5, 0xbe, 0x55, 0x3b, 0x2a, 0x2f, 0xbc, 0xf9, 0x22, 0xab, 0x7d, 0x9a, 0x6c, + 0x99, 0x9b, 0x18, 0x3d, 0xdd, 0x07, 0x28, 0xe7, 0xe1, 0x6d, 0xef, 0xb7, 0xff, 0x64, 0x0a, 0x9b, 0x9d, 0xbb, 0xae, + 0x7e, 0xa0, 0xc5, 0x15, 0xad, 0xaf, 0x53, 0x59, 0x62, 0x78, 0x5f, 0x59, 0xe0, 0x4a, 0x21, 0xed, 0xca, 0xea, 0xe5, + 0xdb, 0x96, 0x39, 0x7d, 0xeb, 0xcd, 0x17, 0x9f, 0x3a, 0x29, 0x00, 0xe8, 0xce, 0x59, 0x41, 0xa5, 0xcf, 0x30, 0xad, + 0x51, 0x6f, 0xff, 0x05, 0xfb, 0xc4, 0x79, 0xed, 0x9a, 0xd2, 0xe7, 0x98, 0x0d, 0xd7, 0xdc, 0xbe, 0x1a, 0x8d, 0xb2, + 0xb4, 0xa4, 0x72, 0x7b, 0xf0, 0x0e, 0x3b, 0xad, 0x94, 0x70, 0xf6, 0xa2, 0x67, 0xeb, 0x14, 0xb6, 0x65, 0x0f, 0x80, + 0xa0, 0x9d, 0x73, 0x0d, 0x38, 0x9a, 0xf1, 0x35, 0xb9, 0x2b, 0x55, 0xbe, 0x5d, 0x41, 0xd6, 0x50, 0x8a, 0x29, 0x2d, + 0xb3, 0x5b, 0x43, 0xa3, 0x7e, 0x38, 0xb7, 0x91, 0xbb, 0xa2, 0x4b, 0x02, 0x05, 0x6f, 0x4c, 0x40, 0xe9, 0x52, 0x92, + 0xa2, 0x6f, 0x5c, 0xff, 0x66, 0x3f, 0x81, 0xaa, 0x99, 0x82, 0x21, 0x69, 0xfe, 0xf3, 0x88, 0x37, 0xd2, 0xe5, 0xfd, + 0x69, 0x37, 0xa6, 0x0a, 0x7b, 0xdb, 0x64, 0x5e, 0xfd, 0xe3, 0x6e, 0xf3, 0xea, 0x8b, 0xbd, 0xcc, 0xab, 0x7f, 0xfc, + 0xec, 0xe6, 0xd5, 0x6f, 0x65, 0xf3, 0x6a, 0xd8, 0xc4, 0x6f, 0xd8, 0x5e, 0x46, 0xcf, 0xc2, 0xc8, 0x28, 0xbc, 0x8d, + 0x07, 0x0e, 0x17, 0x7a, 0xe2, 0xc9, 0x82, 0x81, 0x16, 0x89, 0x83, 0xcb, 0x0f, 0xe7, 0x60, 0x9b, 0xdc, 0x6c, 0x7d, + 0xfc, 0xb9, 0x6c, 0x8f, 0xfd, 0x70, 0xae, 0x4a, 0xc1, 0xd2, 0x03, 0x11, 0x2c, 0x1d, 0x1c, 0xc4, 0x7f, 0xb9, 0x73, + 0x5e, 0x5e, 0x3a, 0xfd, 0xb6, 0x03, 0xc1, 0x46, 0x40, 0x31, 0x80, 0x05, 0x76, 0xbf, 0xdd, 0x86, 0x82, 0x5b, 0xa9, + 0xa0, 0x05, 0x05, 0x9e, 0x54, 0xd0, 0x81, 0x82, 0x89, 0x54, 0x70, 0x04, 0x05, 0x53, 0xa9, 0xe0, 0x18, 0x0a, 0x6e, + 0xd4, 0xf4, 0x32, 0xc8, 0x8c, 0xc7, 0x8f, 0xf5, 0xab, 0x42, 0x9e, 0x8c, 0x3c, 0xff, 0x3c, 0xaf, 0x72, 0x6c, 0x88, + 0xa0, 0x8d, 0xe6, 0xa1, 0xce, 0xcd, 0xff, 0x46, 0x5f, 0x8c, 0xc0, 0x9d, 0x1a, 0x94, 0x7a, 0x06, 0xa8, 0x44, 0xa9, + 0x66, 0x5b, 0xbc, 0x56, 0x7b, 0xaa, 0x9e, 0x7d, 0xa0, 0x25, 0xec, 0xfe, 0x7a, 0xe8, 0x4a, 0x23, 0x2a, 0x77, 0x9e, + 0x2f, 0xb2, 0x08, 0x4e, 0xeb, 0x41, 0xee, 0x91, 0xd6, 0x86, 0x38, 0xb6, 0x70, 0x35, 0xfd, 0x1a, 0xf9, 0x03, 0x2b, + 0x09, 0xc1, 0xe1, 0x48, 0x44, 0x2e, 0x12, 0x1f, 0x50, 0x54, 0xfd, 0xd2, 0xbe, 0xea, 0xbb, 0x79, 0x90, 0x29, 0x1e, + 0xef, 0x8c, 0x46, 0xbf, 0xcc, 0xc2, 0x48, 0x91, 0x98, 0xbb, 0x36, 0x12, 0x77, 0xde, 0x5b, 0x18, 0xa4, 0xe3, 0xee, + 0xcd, 0x21, 0x2e, 0xe8, 0xe9, 0xb4, 0xb7, 0x32, 0x6e, 0x17, 0x2c, 0xe8, 0xcd, 0xb8, 0x39, 0x28, 0xac, 0x3f, 0x59, + 0xf1, 0x2c, 0x75, 0x61, 0x94, 0x86, 0x7b, 0x22, 0x7f, 0x4b, 0xa3, 0x34, 0xb3, 0xad, 0x94, 0x5b, 0x4e, 0x69, 0xb2, + 0xfe, 0xfb, 0x73, 0xd8, 0xb9, 0xbc, 0x66, 0xe3, 0xf5, 0x5c, 0x39, 0x0f, 0xe7, 0x3b, 0x6d, 0x5a, 0xe4, 0x57, 0x30, + 0x4a, 0x95, 0x2e, 0xfa, 0x4c, 0xb1, 0xbd, 0xf9, 0xb7, 0xe8, 0x31, 0x2d, 0xd6, 0x4f, 0x60, 0x6c, 0x4a, 0x42, 0x28, + 0x1b, 0xbe, 0x03, 0xd0, 0x96, 0x8c, 0x4a, 0xce, 0x01, 0x7e, 0xd2, 0xf3, 0x85, 0x2b, 0x8d, 0x67, 0xf8, 0x3d, 0x8b, + 0x63, 0x77, 0x2e, 0xea, 0x57, 0xc7, 0x09, 0x3e, 0x36, 0x99, 0xa4, 0x8f, 0x00, 0x04, 0x9d, 0xb1, 0x57, 0xb1, 0x05, + 0x02, 0x53, 0x66, 0x30, 0x7b, 0x83, 0x45, 0xcb, 0x0d, 0x67, 0x3c, 0x0b, 0x96, 0xa7, 0x68, 0xe2, 0x02, 0x48, 0xe4, + 0x86, 0xf9, 0xe5, 0xc2, 0xc4, 0x9d, 0x97, 0x8b, 0x68, 0xad, 0x53, 0x79, 0x6c, 0x99, 0x85, 0x49, 0xa1, 0xf0, 0x53, + 0x4c, 0x26, 0xfc, 0x70, 0xfe, 0xbb, 0xda, 0x4b, 0x6c, 0xb1, 0x73, 0x79, 0x1f, 0x18, 0x41, 0x32, 0xb2, 0x10, 0xc6, + 0x8a, 0x05, 0x20, 0xec, 0x05, 0xc9, 0xc2, 0x44, 0xef, 0x6e, 0xad, 0x15, 0xe8, 0x86, 0x85, 0x6b, 0xbb, 0x29, 0xc7, + 0xb4, 0xe8, 0x45, 0xf3, 0xb1, 0xab, 0x39, 0xad, 0x63, 0x43, 0xfc, 0xb1, 0xec, 0x8e, 0x9e, 0x62, 0x0f, 0xca, 0xd4, + 0xbb, 0xd9, 0xcc, 0xc2, 0x20, 0x31, 0x67, 0xee, 0xd2, 0xf3, 0xef, 0x7b, 0xcb, 0x30, 0x08, 0xe3, 0x95, 0x3b, 0x61, + 0xfd, 0x5c, 0x75, 0xd3, 0xc7, 0x68, 0x49, 0xdc, 0x61, 0xdf, 0xb1, 0x5a, 0x11, 0x5b, 0x52, 0xeb, 0x2c, 0x18, 0xd2, + 0xcc, 0x67, 0x77, 0x29, 0xff, 0x7c, 0xa1, 0x32, 0x55, 0xc5, 0x2d, 0x47, 0x2d, 0x40, 0x0e, 0xe1, 0x91, 0x96, 0x20, + 0xbe, 0x60, 0x9f, 0x33, 0xf3, 0x3d, 0xab, 0xd5, 0x89, 0xd8, 0x52, 0xb1, 0x3a, 0x8d, 0x9d, 0x47, 0xe1, 0xed, 0x10, + 0x46, 0x8b, 0x8d, 0xcd, 0x98, 0xf9, 0x33, 0x7c, 0x63, 0xa2, 0x73, 0xa7, 0xe8, 0xc7, 0x44, 0x95, 0x0f, 0xf4, 0xc6, + 0x96, 0x7d, 0x78, 0xdd, 0x6b, 0x29, 0x76, 0x7f, 0xe9, 0x05, 0x26, 0x4d, 0xe7, 0xd8, 0x5e, 0x49, 0x7d, 0xc9, 0xf0, + 0xd3, 0x37, 0x58, 0xdd, 0x51, 0xec, 0x3e, 0x88, 0xf6, 0x33, 0x3f, 0xbc, 0xed, 0x2d, 0xbc, 0xe9, 0x94, 0x05, 0x7d, + 0x1c, 0x73, 0x56, 0xc8, 0x7c, 0xdf, 0x5b, 0xc5, 0x5e, 0xdc, 0x5f, 0xba, 0x77, 0xbc, 0xd7, 0xc3, 0xa6, 0x5e, 0xdb, + 0xbc, 0xd7, 0xf6, 0xde, 0xbd, 0x4a, 0xdd, 0x80, 0x23, 0x29, 0xf5, 0xc3, 0x87, 0xd6, 0x51, 0xec, 0xd2, 0x3c, 0xf7, + 0xee, 0x75, 0x15, 0xb1, 0xcd, 0xd2, 0x8d, 0xe6, 0x5e, 0xd0, 0xb3, 0x53, 0xeb, 0x66, 0x43, 0x1b, 0xe3, 0x71, 0xb7, + 0xdb, 0x4d, 0xad, 0xa9, 0x78, 0xb2, 0xa7, 0xd3, 0xd4, 0x9a, 0x88, 0xa7, 0xd9, 0xcc, 0xb6, 0x67, 0xb3, 0xd4, 0xf2, + 0x44, 0x41, 0xbb, 0x35, 0x99, 0xb6, 0x5b, 0xa9, 0x75, 0x2b, 0xd5, 0x48, 0x2d, 0xc6, 0x9f, 0x22, 0x36, 0xed, 0xe3, + 0x46, 0xe2, 0x76, 0xe9, 0xc7, 0xb6, 0x9d, 0x22, 0x06, 0xb8, 0x2c, 0xe0, 0x26, 0xd4, 0x2a, 0x5e, 0x6d, 0xf6, 0xae, + 0xa9, 0xe4, 0x9f, 0x9b, 0x4c, 0x6a, 0xeb, 0x4d, 0xdd, 0xe8, 0xc3, 0x95, 0x22, 0xcd, 0xc2, 0x75, 0xa9, 0xda, 0x46, + 0x80, 0xc1, 0xbc, 0xeb, 0x41, 0xd4, 0xcc, 0xfe, 0x38, 0x8c, 0xe0, 0xcc, 0x46, 0xee, 0xd4, 0x5b, 0xc7, 0x3d, 0xa7, + 0xb5, 0xba, 0x13, 0x45, 0x7c, 0xaf, 0xe7, 0x05, 0x78, 0xf6, 0x7a, 0x71, 0xe8, 0x7b, 0x53, 0x51, 0xd4, 0x74, 0x96, + 0x9c, 0x96, 0xde, 0xc7, 0x98, 0x31, 0x1e, 0x46, 0x3e, 0x72, 0x7d, 0x5f, 0xb1, 0xda, 0xb1, 0xc2, 0xdc, 0x18, 0x6f, + 0x32, 0x14, 0x3b, 0x26, 0xb8, 0x60, 0x7c, 0x18, 0xe7, 0x70, 0x75, 0x97, 0xed, 0x79, 0xe7, 0x68, 0x75, 0x97, 0x7e, + 0xb5, 0x64, 0x53, 0xcf, 0x55, 0xb4, 0x7c, 0x37, 0x39, 0x36, 0xdc, 0x76, 0xe8, 0x9b, 0x86, 0x6d, 0x2a, 0x8e, 0x05, + 0x44, 0x17, 0x7e, 0xe4, 0x2d, 0x57, 0x61, 0x94, 0xb8, 0x41, 0x92, 0xa6, 0xa3, 0xab, 0x34, 0xed, 0x5f, 0x78, 0xda, + 0xe5, 0x3f, 0x34, 0xa2, 0x85, 0x74, 0x3b, 0x98, 0xea, 0x57, 0xc6, 0x1b, 0x26, 0x5b, 0x32, 0x01, 0x19, 0x43, 0x2b, + 0x26, 0xb9, 0x32, 0xd1, 0xdb, 0x6a, 0x65, 0x02, 0x72, 0x56, 0x9d, 0x0c, 0xa3, 0x8a, 0x55, 0x90, 0x02, 0x41, 0x85, + 0x37, 0x6c, 0x70, 0x21, 0x99, 0x45, 0x01, 0xd3, 0x83, 0x95, 0xc9, 0xb5, 0xef, 0x49, 0x13, 0xef, 0xf9, 0xf5, 0x6e, + 0xde, 0xf3, 0x9f, 0xc9, 0x3e, 0xbc, 0xe7, 0xd7, 0x9f, 0x9d, 0xf7, 0x7c, 0x52, 0x75, 0xed, 0x3b, 0x0b, 0x07, 0x6a, + 0x76, 0x97, 0x05, 0xa4, 0x29, 0xa2, 0xa0, 0x79, 0x67, 0xc9, 0x7f, 0xeb, 0x8a, 0x27, 0x7a, 0xa3, 0x34, 0xb0, 0x44, + 0xb9, 0x81, 0x81, 0x7f, 0x1b, 0x0c, 0xfe, 0x1e, 0xc9, 0xcf, 0xb3, 0xd9, 0xe0, 0x75, 0x28, 0x15, 0x64, 0x4f, 0xdc, + 0xcc, 0xa7, 0x10, 0xe0, 0x88, 0xde, 0x64, 0x86, 0x58, 0x90, 0x02, 0x0a, 0xe2, 0xa3, 0x90, 0xb1, 0xfd, 0x34, 0x33, + 0x87, 0xec, 0x17, 0x87, 0xa0, 0x65, 0x06, 0xc6, 0xc2, 0x0b, 0xb6, 0xa2, 0xb4, 0x9e, 0xb3, 0x84, 0x87, 0xad, 0x78, + 0x79, 0x7f, 0x36, 0xd5, 0xce, 0x42, 0x3d, 0xf5, 0xe2, 0xb7, 0x65, 0x1f, 0x54, 0x21, 0x82, 0xc8, 0xd3, 0x49, 0xb9, + 0x49, 0xa3, 0x14, 0x6a, 0x06, 0x5f, 0x53, 0xf3, 0xd3, 0xc2, 0x4c, 0x7b, 0x72, 0x43, 0x9e, 0x6b, 0xb2, 0x42, 0x8c, + 0xb9, 0x4b, 0xdf, 0x86, 0x73, 0x79, 0x98, 0x3e, 0x13, 0x43, 0x77, 0x4c, 0xa9, 0xb9, 0x37, 0x4d, 0x53, 0xbd, 0x2f, + 0x00, 0x21, 0x11, 0x5a, 0xb6, 0x8b, 0x89, 0x8b, 0x73, 0x81, 0x96, 0xdf, 0x45, 0xd3, 0x45, 0xf3, 0x19, 0x98, 0x6e, + 0xf0, 0x6b, 0x69, 0x0e, 0x33, 0x55, 0x21, 0xf0, 0x91, 0x49, 0x8f, 0x34, 0x21, 0xb0, 0x35, 0x90, 0x0d, 0xe1, 0x0a, + 0x0b, 0x52, 0x35, 0x2a, 0x26, 0xe0, 0xa0, 0xed, 0x09, 0x04, 0xda, 0x11, 0xda, 0x2e, 0x42, 0x3b, 0xbc, 0x0e, 0x3e, + 0xa4, 0x6a, 0xc6, 0xfb, 0xe1, 0xf6, 0x1b, 0x9e, 0x1c, 0x40, 0x83, 0x61, 0x49, 0x93, 0xb5, 0xc3, 0x64, 0x16, 0x58, + 0x89, 0xf8, 0xd6, 0xb0, 0xe2, 0x5b, 0xe5, 0xd9, 0x46, 0x04, 0xa9, 0x4a, 0xdc, 0x95, 0x09, 0xea, 0x13, 0xc4, 0xbd, + 0x1c, 0xe3, 0x49, 0xf1, 0xb0, 0xfa, 0xeb, 0x18, 0x70, 0x23, 0x4a, 0xf2, 0x88, 0x7f, 0xfa, 0x93, 0x75, 0x14, 0x87, + 0x51, 0x6f, 0x15, 0x7a, 0x41, 0xc2, 0xa2, 0x14, 0x41, 0x75, 0x89, 0xf0, 0x11, 0xe0, 0xb9, 0xda, 0x84, 0x2b, 0x77, + 0xe2, 0x25, 0xf7, 0x3d, 0x9b, 0xb3, 0x14, 0x76, 0x9f, 0x73, 0x07, 0x76, 0x6d, 0xfd, 0x1e, 0x87, 0xe6, 0x73, 0x64, + 0xfc, 0xa2, 0x2a, 0x3b, 0x23, 0x6f, 0xf3, 0xbe, 0xf4, 0x96, 0xc2, 0x74, 0x01, 0xfb, 0xe1, 0x46, 0xe6, 0x1c, 0xb0, + 0x3c, 0x2c, 0xb5, 0x3d, 0x65, 0x73, 0x03, 0xb1, 0x36, 0xdc, 0x00, 0x89, 0x3f, 0x56, 0x47, 0x57, 0xec, 0xfa, 0x62, + 0xe0, 0x78, 0xf4, 0x7d, 0x46, 0xd6, 0x73, 0x21, 0xa9, 0xa5, 0xb1, 0x4f, 0xcd, 0x31, 0x9b, 0x85, 0x11, 0xa3, 0x90, + 0xee, 0x4e, 0x77, 0x75, 0xb7, 0x7f, 0xf7, 0xdb, 0xa7, 0x5f, 0xdf, 0x4f, 0x10, 0x26, 0x9a, 0xe8, 0x4c, 0xdf, 0xd1, + 0x5b, 0x95, 0x9e, 0x01, 0x6b, 0x48, 0x90, 0x9f, 0x90, 0xc3, 0x49, 0x4f, 0x55, 0xfb, 0xb5, 0x91, 0x33, 0x57, 0x21, + 0xa7, 0x79, 0x11, 0xf3, 0xdd, 0xc4, 0xbb, 0x11, 0x3c, 0x63, 0xfb, 0x68, 0x75, 0x27, 0xd6, 0x18, 0x09, 0xde, 0x03, + 0x16, 0xa9, 0x34, 0x14, 0xb1, 0x48, 0xe5, 0x62, 0x5c, 0xa4, 0x7e, 0x65, 0x36, 0x22, 0x98, 0x54, 0x89, 0xd2, 0x77, + 0x56, 0x77, 0x32, 0x89, 0xce, 0x9b, 0x65, 0x94, 0xba, 0x1c, 0x05, 0x74, 0xe9, 0x4d, 0xa7, 0x3e, 0x4b, 0x0b, 0x0b, + 0x5d, 0x5c, 0x4b, 0x09, 0x38, 0x19, 0x1c, 0xdc, 0x71, 0x1c, 0xfa, 0xeb, 0x84, 0xd5, 0x83, 0x8b, 0x80, 0xd3, 0xb2, + 0x73, 0xe0, 0xe0, 0xef, 0xe2, 0x58, 0x3b, 0xc0, 0x6e, 0xc3, 0x36, 0xb1, 0xfb, 0x10, 0xf4, 0xdf, 0x6c, 0x17, 0x87, + 0x0e, 0xaf, 0xb2, 0x41, 0x1b, 0x35, 0x13, 0x31, 0x80, 0x2c, 0x11, 0xf6, 0x56, 0x2c, 0x87, 0x97, 0x65, 0x81, 0xcf, + 0xb3, 0xa2, 0xb4, 0x38, 0x99, 0xdf, 0xe7, 0x8c, 0xbd, 0xa8, 0x3f, 0x63, 0x2f, 0xc4, 0x19, 0xdb, 0xbe, 0x33, 0x1f, + 0xcf, 0x1c, 0xf8, 0xaf, 0x9f, 0x4f, 0xa8, 0x67, 0x2b, 0xed, 0xd5, 0x9d, 0xe2, 0xac, 0xee, 0x14, 0xb3, 0xb5, 0xba, + 0x53, 0xb0, 0x6b, 0xb4, 0x3c, 0x32, 0xac, 0x96, 0x6e, 0xd8, 0x0a, 0x14, 0xc2, 0x1f, 0xbb, 0xf0, 0xca, 0x39, 0x84, + 0x77, 0xd0, 0xaa, 0x53, 0x7d, 0xd7, 0xda, 0x7e, 0xd4, 0xe9, 0x2c, 0x09, 0xa4, 0xad, 0x5b, 0x89, 0x3b, 0x1e, 0xb3, + 0x69, 0x6f, 0x16, 0x4e, 0xd6, 0xf1, 0xbf, 0xf9, 0xf8, 0x39, 0x10, 0xb7, 0x22, 0x82, 0x52, 0x3f, 0xa2, 0x29, 0x68, + 0xf7, 0x6e, 0x98, 0xe8, 0x61, 0x93, 0xad, 0x53, 0x8f, 0x32, 0x14, 0xb4, 0xac, 0xc3, 0x9a, 0x4d, 0x5e, 0x0f, 0xe8, + 0xdf, 0x6d, 0x95, 0x9a, 0x51, 0xcc, 0x27, 0x80, 0x65, 0x2b, 0x38, 0x1e, 0x0e, 0x0d, 0xbe, 0x9a, 0x76, 0xb7, 0x7e, + 0xb8, 0x97, 0xe2, 0x4b, 0x57, 0x82, 0xa8, 0x70, 0xba, 0xc5, 0xdd, 0xa0, 0xb6, 0xf7, 0xda, 0xb4, 0x47, 0x2a, 0xbd, + 0x6e, 0x21, 0x08, 0x79, 0xdd, 0x3d, 0xb1, 0xfc, 0xe3, 0x17, 0x87, 0xf0, 0x1f, 0x71, 0xf5, 0xff, 0x4c, 0xea, 0x18, + 0xf5, 0xb3, 0xa4, 0xc0, 0xa8, 0x13, 0xab, 0x84, 0x8c, 0xf8, 0xfe, 0xf5, 0x67, 0xb3, 0x87, 0x35, 0xd8, 0xbb, 0x36, + 0x19, 0xed, 0x95, 0x6b, 0xbf, 0x0c, 0x43, 0xc8, 0x9e, 0x5d, 0xad, 0x2e, 0xc0, 0x43, 0x1e, 0x18, 0xc9, 0x00, 0x1a, + 0x09, 0x39, 0x82, 0xec, 0x45, 0x54, 0x6c, 0x43, 0xa2, 0xc4, 0x9b, 0x26, 0x51, 0xe2, 0xf5, 0x6e, 0x51, 0xe2, 0xbb, + 0xbd, 0x44, 0x89, 0xd7, 0x9f, 0x5d, 0x94, 0x78, 0x53, 0x15, 0x25, 0x2e, 0x42, 0x61, 0xa9, 0x6d, 0x9c, 0xad, 0xf9, + 0xcf, 0x9f, 0xe9, 0x2a, 0xf6, 0x3c, 0x1c, 0x74, 0x6c, 0xca, 0x3a, 0x70, 0xf1, 0x5f, 0x0b, 0x16, 0xb8, 0x11, 0xdf, + 0xa1, 0xe1, 0x62, 0x2e, 0x5a, 0x70, 0xcc, 0x8e, 0xdf, 0x91, 0x8a, 0xfd, 0x30, 0x98, 0xff, 0x08, 0x57, 0xf1, 0xa0, + 0x0e, 0x8c, 0xa4, 0x17, 0x5e, 0xfc, 0x63, 0xb8, 0x5a, 0xaf, 0xce, 0xa0, 0xaf, 0x9f, 0xbd, 0xd8, 0x1b, 0xfb, 0x2c, + 0x8b, 0x06, 0x42, 0x86, 0x96, 0x5c, 0xb7, 0x0e, 0xb6, 0xcd, 0xe2, 0xa7, 0x7b, 0x27, 0x7e, 0xa2, 0xf5, 0x33, 0xff, + 0x4d, 0x16, 0x9c, 0x6a, 0xbd, 0x20, 0x02, 0x61, 0xf3, 0x4a, 0x83, 0x7e, 0xb8, 0x30, 0x72, 0x11, 0xea, 0x35, 0xb3, + 0x14, 0x96, 0x35, 0x8d, 0xfd, 0xb0, 0x8a, 0x50, 0xb3, 0xd6, 0x8d, 0x2c, 0x0a, 0x66, 0x55, 0x9d, 0xbf, 0x0c, 0xd7, + 0x31, 0x9b, 0x86, 0xb7, 0x81, 0x6a, 0x04, 0xdc, 0x1c, 0x94, 0x12, 0x09, 0x66, 0x6d, 0x30, 0x7f, 0xf3, 0x7b, 0x64, + 0x94, 0x21, 0x62, 0x02, 0xa4, 0x0f, 0x5f, 0xaf, 0x4c, 0x32, 0x30, 0x30, 0x71, 0x8a, 0x6a, 0x96, 0x68, 0xf0, 0x91, + 0xa6, 0x85, 0x83, 0x87, 0xb5, 0x14, 0x46, 0x41, 0xa1, 0xc5, 0xb5, 0xc2, 0xb1, 0x16, 0x08, 0xe5, 0xa2, 0x08, 0x45, + 0x55, 0xb3, 0x70, 0xfc, 0x0d, 0x85, 0xfa, 0xc8, 0xdf, 0x42, 0x64, 0x88, 0x74, 0xcd, 0xd7, 0x83, 0x07, 0x66, 0xa2, + 0xc7, 0x57, 0x12, 0x18, 0xdf, 0xde, 0xb0, 0xc8, 0x77, 0xef, 0x35, 0x3d, 0x0d, 0x83, 0xef, 0x01, 0x00, 0xaf, 0xc3, + 0xdb, 0x40, 0xae, 0x80, 0xf9, 0xd2, 0x6a, 0xf6, 0x52, 0x6d, 0x08, 0x31, 0x70, 0xa7, 0x92, 0x46, 0x00, 0x99, 0xea, + 0xe7, 0xec, 0xef, 0x06, 0xfd, 0xfb, 0x0f, 0x3d, 0x35, 0xce, 0xc3, 0xec, 0x43, 0x3f, 0xad, 0xf6, 0xf8, 0xcc, 0xd3, + 0xa7, 0x8f, 0x9a, 0xa7, 0xad, 0x4d, 0x7c, 0xe6, 0x8a, 0xfc, 0xf3, 0x5a, 0x4d, 0x6b, 0xbd, 0xf1, 0x14, 0xc0, 0x28, + 0x2e, 0xc2, 0xf5, 0x64, 0x81, 0x26, 0xd5, 0x9f, 0x6f, 0xbe, 0x09, 0xf4, 0x89, 0x89, 0xc2, 0xb3, 0xa9, 0x97, 0x8a, + 0x72, 0x28, 0xe0, 0xf7, 0xdf, 0x40, 0x0c, 0xec, 0x3f, 0x11, 0x0c, 0xd5, 0x5d, 0x93, 0xb9, 0x5b, 0x3f, 0x68, 0xf3, + 0xf6, 0x21, 0x9f, 0x35, 0x8f, 0x2e, 0x25, 0x2e, 0xe9, 0xea, 0x91, 0x4c, 0x5a, 0x06, 0x9a, 0x1c, 0xc9, 0xb5, 0x29, + 0x48, 0xad, 0xf8, 0x0a, 0xb3, 0x48, 0x4c, 0xe7, 0x2e, 0x2d, 0x06, 0xe3, 0xd8, 0xaa, 0x84, 0x64, 0xb8, 0xa1, 0x0b, + 0x43, 0xf4, 0x55, 0x7e, 0xb7, 0xf4, 0x02, 0x03, 0x13, 0xb1, 0x54, 0xdf, 0xb8, 0x77, 0x90, 0x8a, 0x00, 0x90, 0x5b, + 0xf9, 0x15, 0x14, 0x1a, 0xb2, 0x23, 0x27, 0x64, 0x5b, 0x54, 0x6b, 0x21, 0x21, 0x6e, 0x03, 0x47, 0x5f, 0x28, 0x8a, + 0xa2, 0x64, 0x62, 0x84, 0x92, 0xc9, 0x11, 0x58, 0x8e, 0xe2, 0x00, 0xdc, 0x96, 0xa4, 0xab, 0x3b, 0x2a, 0x01, 0xc9, + 0x00, 0xaf, 0xb6, 0x45, 0x01, 0x8f, 0xb6, 0xdb, 0xb1, 0x45, 0x81, 0x10, 0xe8, 0x21, 0x52, 0xaa, 0x1b, 0x41, 0x50, + 0xfe, 0x9e, 0x82, 0x02, 0x3b, 0xbe, 0xe5, 0x9a, 0x60, 0xc5, 0xa6, 0xc7, 0x51, 0x9f, 0xd5, 0x87, 0x65, 0x0d, 0x24, + 0x2c, 0x08, 0xb7, 0x0e, 0xa5, 0x2c, 0x0b, 0x06, 0xab, 0xc1, 0x8d, 0x28, 0x17, 0xdd, 0x25, 0x4b, 0x16, 0xac, 0x55, + 0x4c, 0xcb, 0x88, 0x61, 0x72, 0xa1, 0xce, 0x6b, 0x62, 0xb6, 0x00, 0xdb, 0xd4, 0xb7, 0x5c, 0x10, 0x2d, 0x8c, 0x39, + 0x4a, 0x75, 0x8d, 0x09, 0xf7, 0x4d, 0x8c, 0x39, 0x6e, 0x2b, 0x53, 0x08, 0xbe, 0xa4, 0x61, 0x11, 0x9b, 0x73, 0x6f, + 0x64, 0xe4, 0x14, 0x28, 0x42, 0x15, 0x57, 0x17, 0x09, 0xb0, 0x6b, 0x6e, 0x79, 0xd1, 0xb2, 0x1b, 0x19, 0xb7, 0xa4, + 0x28, 0x8a, 0xf4, 0x6a, 0x37, 0x7c, 0x9c, 0x10, 0x1b, 0xb0, 0xb1, 0x9f, 0x49, 0xa5, 0x9f, 0x86, 0x49, 0x7f, 0x60, + 0xf7, 0x44, 0x48, 0x08, 0x54, 0x1f, 0xd8, 0x3d, 0xdc, 0xdb, 0xbf, 0x01, 0x6d, 0x8a, 0xba, 0x05, 0x5d, 0x1b, 0x90, + 0x6d, 0x67, 0x02, 0xf1, 0x22, 0xb7, 0x1c, 0x20, 0x3b, 0xdd, 0x82, 0xc5, 0x11, 0xc4, 0x81, 0x11, 0xf7, 0xc5, 0x21, + 0xe6, 0xce, 0x24, 0x5a, 0x2d, 0x8c, 0xcd, 0x9a, 0xa3, 0xa1, 0x3f, 0x73, 0x6c, 0xfb, 0xa0, 0x52, 0x1f, 0x14, 0xd9, + 0x75, 0xb5, 0x75, 0x23, 0x19, 0x38, 0xb6, 0xe9, 0x3d, 0xb3, 0x5a, 0xfd, 0x0a, 0x8d, 0x96, 0xc2, 0x39, 0x8f, 0x50, + 0xfd, 0x35, 0x7c, 0xb2, 0xd1, 0x2a, 0x07, 0x52, 0x2f, 0x3b, 0x67, 0xe0, 0xd8, 0x52, 0xae, 0xff, 0x1a, 0x55, 0x49, + 0x3f, 0x05, 0x93, 0xa6, 0xd4, 0x62, 0x23, 0x48, 0x48, 0xa0, 0xc1, 0x31, 0xfa, 0x8b, 0xf2, 0x5c, 0xd1, 0xe8, 0xf8, + 0xe8, 0xfa, 0xa8, 0x2f, 0x30, 0x8a, 0xf0, 0x5e, 0x94, 0x3b, 0x28, 0x7d, 0x31, 0x2e, 0x63, 0x38, 0x1e, 0xfa, 0x9c, + 0xe5, 0x37, 0x7a, 0x5b, 0xb9, 0x05, 0xec, 0xbf, 0x81, 0x7c, 0x5a, 0x63, 0x88, 0xaf, 0x01, 0x35, 0x20, 0x7d, 0xc9, + 0xce, 0x0e, 0x21, 0x6c, 0x92, 0xdc, 0x5d, 0x91, 0x48, 0xee, 0xdf, 0x19, 0x12, 0x1d, 0xbc, 0x43, 0xcb, 0xfa, 0xab, + 0x27, 0x77, 0x0f, 0xec, 0x92, 0x05, 0xd3, 0x62, 0x87, 0x25, 0xfa, 0xb5, 0x7f, 0x77, 0x05, 0x8c, 0x02, 0x79, 0x7d, + 0xc2, 0x1a, 0x8c, 0x92, 0x86, 0x01, 0x6e, 0x7e, 0x3a, 0x6e, 0xde, 0x5e, 0x5c, 0x0c, 0x36, 0xa0, 0xa0, 0x9c, 0x59, + 0x33, 0x49, 0x29, 0x0e, 0xc9, 0x23, 0xd0, 0xb9, 0x59, 0x13, 0x8c, 0x68, 0xe3, 0x4e, 0x4c, 0x84, 0x25, 0x69, 0xde, + 0xc6, 0xe3, 0xe1, 0xa0, 0xf7, 0xd5, 0x5a, 0x7b, 0xbb, 0xb5, 0xd6, 0xc9, 0x2e, 0xad, 0x35, 0x39, 0xee, 0x91, 0xf9, + 0x53, 0xe6, 0xc0, 0x28, 0x98, 0x73, 0xd9, 0x05, 0xb4, 0xa0, 0xea, 0x46, 0x3f, 0x3f, 0xd1, 0xaa, 0xd2, 0x1b, 0xd9, + 0x86, 0xa2, 0xfa, 0x5b, 0x12, 0x50, 0xc4, 0x85, 0xba, 0xac, 0x1b, 0xbf, 0xc8, 0x75, 0xe3, 0x24, 0xd5, 0xe4, 0x2e, + 0x5b, 0x82, 0xfb, 0x97, 0xdc, 0x21, 0x33, 0xe9, 0x20, 0x77, 0x8b, 0xcc, 0x47, 0x2a, 0x39, 0xfa, 0xe5, 0x82, 0x86, + 0xe4, 0x3e, 0x2a, 0xa4, 0x8c, 0xa2, 0x17, 0x69, 0xb1, 0x6a, 0xee, 0xe7, 0x97, 0x97, 0x83, 0xd6, 0x1d, 0x87, 0x9c, + 0x15, 0xcb, 0xdb, 0xa6, 0xe8, 0xe8, 0x25, 0xbf, 0x96, 0x36, 0x49, 0xe6, 0x91, 0x45, 0x00, 0x16, 0x6a, 0xfa, 0xd2, + 0xbd, 0x76, 0x66, 0x03, 0x81, 0x83, 0xac, 0x71, 0x20, 0xdd, 0xad, 0x9d, 0xa7, 0x94, 0x45, 0xf9, 0xd5, 0xb5, 0x83, + 0xd4, 0x9d, 0x6e, 0x82, 0x65, 0x7d, 0x04, 0xc2, 0xfa, 0x4a, 0xd2, 0x20, 0xf4, 0x6c, 0xc5, 0xee, 0xd7, 0x30, 0x00, + 0x48, 0xff, 0xcb, 0xcf, 0x9c, 0x15, 0x00, 0x4d, 0xa4, 0x62, 0xcb, 0x77, 0xfe, 0x78, 0x88, 0x4d, 0x32, 0x3f, 0xc3, + 0xaa, 0xd5, 0x6f, 0x92, 0xbe, 0x67, 0xc3, 0xdd, 0xb5, 0x8a, 0xea, 0x7c, 0x5e, 0xa3, 0x27, 0xc6, 0xc1, 0x77, 0x59, + 0xb4, 0x0e, 0x30, 0x13, 0x8d, 0x99, 0x44, 0xee, 0xe4, 0xc3, 0x46, 0xfa, 0x1e, 0x57, 0x89, 0x82, 0xba, 0xb8, 0x78, + 0xa9, 0xd0, 0x77, 0x31, 0x70, 0x33, 0xeb, 0x59, 0xad, 0x58, 0x52, 0xd4, 0xf4, 0x1e, 0xdb, 0x6d, 0xf7, 0xc5, 0xec, + 0xb0, 0xa4, 0x3f, 0x6d, 0x75, 0x8a, 0xda, 0xf5, 0x6c, 0x1c, 0xcb, 0xf0, 0x57, 0xee, 0xd8, 0xfa, 0xc7, 0x7f, 0x3a, + 0xe6, 0xdf, 0x2c, 0xad, 0xd1, 0xa7, 0x0c, 0x01, 0xda, 0x17, 0x2e, 0xa6, 0xe5, 0x6b, 0x9a, 0x4a, 0x49, 0xd3, 0xb0, + 0x66, 0x9e, 0xef, 0x9b, 0x3e, 0xb8, 0x17, 0x6d, 0x3e, 0x69, 0x7a, 0xd8, 0xcf, 0x1a, 0x52, 0x06, 0x7c, 0x42, 0x3f, + 0xc5, 0x9d, 0x92, 0x2c, 0xd6, 0xcb, 0xf1, 0x46, 0x56, 0x94, 0x4b, 0xfa, 0xf3, 0xaa, 0xce, 0x5c, 0xfe, 0xec, 0x6c, + 0x36, 0x2b, 0x6a, 0x8d, 0x6d, 0xe5, 0x10, 0x35, 0xbf, 0x8f, 0x6d, 0xdb, 0x2e, 0xc3, 0xb7, 0xe9, 0xa0, 0xd0, 0xc1, + 0x30, 0x51, 0x09, 0xdf, 0xdd, 0xbd, 0xa7, 0xfe, 0xa0, 0xd1, 0x52, 0x57, 0x4d, 0xe7, 0x91, 0xb6, 0xda, 0xff, 0x8b, + 0xa1, 0x20, 0x6a, 0xd8, 0x75, 0xfc, 0xab, 0x7b, 0x65, 0x4b, 0x4f, 0xe5, 0x03, 0xfc, 0xb0, 0xc6, 0x3b, 0xf6, 0xfa, + 0x1e, 0x4d, 0x9b, 0xb6, 0x77, 0x6a, 0xe5, 0x64, 0xb7, 0x60, 0xb3, 0xd4, 0x27, 0x4b, 0x25, 0x2f, 0x61, 0xcb, 0xb8, + 0x37, 0x61, 0x78, 0x41, 0x6a, 0x49, 0xd4, 0x16, 0xad, 0x7a, 0xcc, 0x39, 0xd8, 0x71, 0x39, 0x02, 0x0f, 0xdb, 0x0a, + 0x5e, 0x56, 0x55, 0x6e, 0xd6, 0xc4, 0x47, 0x90, 0x8a, 0x6d, 0xaa, 0x17, 0x4e, 0xb8, 0x4d, 0x3b, 0xf6, 0x5f, 0x0a, + 0xf5, 0x14, 0xe0, 0x4e, 0x37, 0xc2, 0xda, 0x84, 0x2e, 0x4f, 0xf0, 0xef, 0xec, 0x72, 0xee, 0xc5, 0xea, 0xae, 0x68, + 0xdc, 0xd5, 0x85, 0xeb, 0xa6, 0x9c, 0x94, 0xd1, 0xa8, 0xeb, 0x50, 0x5f, 0x66, 0x02, 0x34, 0x93, 0xad, 0x5b, 0xc0, + 0x82, 0xa6, 0x90, 0x20, 0xaf, 0xe6, 0x6e, 0x0c, 0xc5, 0x59, 0xd8, 0x79, 0xb9, 0x7e, 0x3f, 0x4f, 0xef, 0x0c, 0x73, + 0x30, 0x9e, 0x77, 0xf1, 0x72, 0xaf, 0xb0, 0x55, 0xd1, 0x54, 0x06, 0xf7, 0x80, 0x90, 0x48, 0x95, 0x75, 0xe4, 0x9b, + 0x94, 0x3d, 0x4e, 0xd3, 0x37, 0xd5, 0x79, 0x37, 0x77, 0xef, 0x74, 0xe0, 0x5e, 0xa3, 0x0a, 0xaa, 0xbd, 0xae, 0xf6, + 0xca, 0x77, 0xd8, 0x62, 0x9c, 0xb0, 0x02, 0xe0, 0x8a, 0xa2, 0xa0, 0xd1, 0x90, 0x52, 0xc2, 0x7d, 0x34, 0xe9, 0xec, + 0xad, 0x8c, 0xac, 0xc5, 0x3c, 0xb1, 0xbb, 0xfa, 0x2a, 0xd4, 0xb7, 0xb8, 0x19, 0x04, 0xd8, 0x71, 0xec, 0x84, 0xcf, + 0x26, 0xec, 0x18, 0x19, 0x5d, 0x39, 0xb8, 0x83, 0xf0, 0x94, 0x9a, 0x14, 0xdf, 0x96, 0x4e, 0x29, 0xde, 0x25, 0x7c, + 0x57, 0xab, 0xbc, 0xbf, 0x28, 0x68, 0xe3, 0xb9, 0x3f, 0x50, 0x4b, 0xdf, 0xab, 0xf6, 0xd2, 0x0b, 0xf6, 0xaf, 0xeb, + 0xde, 0xed, 0x5d, 0x17, 0x98, 0xc3, 0xbd, 0x2b, 0x03, 0x77, 0x49, 0x56, 0x4a, 0xc9, 0xe0, 0x3b, 0xe9, 0xf2, 0x40, + 0x8e, 0x65, 0xa1, 0x62, 0x2b, 0x92, 0xe8, 0x2f, 0xd6, 0x83, 0xd1, 0xc9, 0xe9, 0xdd, 0xd2, 0x57, 0x6e, 0x58, 0x04, + 0x99, 0x34, 0x07, 0xaa, 0x63, 0xd9, 0xaa, 0x82, 0x91, 0x19, 0xbc, 0x60, 0x3e, 0x50, 0x7f, 0xba, 0xf8, 0xc6, 0xec, + 0xaa, 0xa7, 0x60, 0x8e, 0x71, 0x33, 0x47, 0x16, 0xf7, 0xdc, 0xbd, 0x67, 0xd1, 0x75, 0x4b, 0x55, 0x30, 0x61, 0x26, + 0x31, 0xb7, 0x58, 0xa6, 0xb4, 0xd4, 0x3d, 0xf2, 0xb2, 0x29, 0x22, 0xb5, 0xb2, 0x0a, 0x88, 0xd5, 0x69, 0x75, 0x15, + 0xa7, 0x75, 0x68, 0x1d, 0x75, 0xd5, 0xe1, 0x17, 0x8a, 0x72, 0x32, 0x65, 0xb3, 0x78, 0x88, 0xea, 0x98, 0x13, 0xe4, + 0x07, 0xe9, 0xb7, 0xa2, 0x58, 0x13, 0x3f, 0x36, 0x1d, 0x65, 0xc3, 0x1f, 0x15, 0x05, 0x90, 0x51, 0x4f, 0x79, 0x3c, + 0x6b, 0xcd, 0x0e, 0x67, 0x2f, 0xfa, 0xbc, 0x38, 0xfd, 0xa2, 0x50, 0xdd, 0xa0, 0x7f, 0x5b, 0x52, 0xb3, 0x38, 0x89, + 0xc2, 0x0f, 0x8c, 0xf3, 0x92, 0x4a, 0xa6, 0x28, 0x2a, 0x37, 0x6d, 0x55, 0xbf, 0xe4, 0x74, 0xc7, 0x93, 0x59, 0x2b, + 0xaf, 0x8e, 0x63, 0x3c, 0xc8, 0x06, 0x79, 0x72, 0x20, 0x86, 0x7e, 0x22, 0x83, 0xc9, 0x31, 0xeb, 0x00, 0xe5, 0xa8, + 0x7c, 0x8e, 0x73, 0x31, 0xbf, 0x13, 0x08, 0x7b, 0x9e, 0x7b, 0x70, 0xc4, 0xd8, 0x6c, 0xa0, 0x7e, 0xef, 0xb4, 0xba, + 0x86, 0xe3, 0x1c, 0x59, 0x47, 0xdd, 0x89, 0x6d, 0x1c, 0x5a, 0x87, 0x66, 0xdb, 0x3a, 0x32, 0xba, 0x66, 0xd7, 0xe8, + 0x7e, 0xdb, 0x9d, 0x98, 0x87, 0xd6, 0xa1, 0x61, 0x9b, 0x5d, 0x28, 0x34, 0xbb, 0x66, 0xf7, 0xc6, 0x3c, 0xec, 0x4e, + 0x6c, 0x2c, 0x6d, 0x59, 0x9d, 0x8e, 0xe9, 0xd8, 0x56, 0xa7, 0x63, 0x74, 0xac, 0xa3, 0x23, 0xd3, 0x69, 0x5b, 0x47, + 0x47, 0xe7, 0x9d, 0xae, 0xd5, 0x86, 0x77, 0xed, 0xf6, 0xa4, 0x6d, 0x39, 0x8e, 0x09, 0x7f, 0x19, 0x5d, 0xab, 0x45, + 0x3f, 0x1c, 0xc7, 0x6a, 0x3b, 0x86, 0xed, 0x77, 0x5a, 0xd6, 0xd1, 0x0b, 0x03, 0xff, 0xc6, 0x6a, 0x06, 0xfe, 0x05, + 0xdd, 0x18, 0x2f, 0xac, 0xd6, 0x11, 0xfd, 0xc2, 0x0e, 0x6f, 0x0e, 0xbb, 0xff, 0x54, 0x0f, 0x1a, 0xe7, 0xe0, 0xd0, + 0x1c, 0xba, 0x1d, 0xab, 0xdd, 0x36, 0x0e, 0x1d, 0xab, 0xdb, 0x5e, 0x98, 0x87, 0x2d, 0xeb, 0xe8, 0x78, 0x62, 0x3a, + 0xd6, 0xf1, 0xb1, 0x61, 0x9b, 0x6d, 0xab, 0x65, 0x38, 0xd6, 0x61, 0x1b, 0x7f, 0xb4, 0xad, 0xd6, 0xcd, 0xf1, 0x0b, + 0xeb, 0xa8, 0xb3, 0x38, 0xb2, 0x0e, 0x7f, 0x3e, 0xec, 0x5a, 0xad, 0xf6, 0xa2, 0x7d, 0x64, 0xb5, 0x8e, 0x6f, 0x8e, + 0xac, 0xc3, 0x85, 0xd9, 0x3a, 0xda, 0xda, 0xd2, 0x69, 0x59, 0x00, 0x23, 0x7c, 0x0d, 0x2f, 0x0c, 0xfe, 0x02, 0xfe, + 0x2c, 0xb0, 0xed, 0x1f, 0xd8, 0x4d, 0x5c, 0x6d, 0xfa, 0xc2, 0xea, 0x1e, 0x4f, 0xa8, 0x3a, 0x14, 0x98, 0xa2, 0x06, + 0x34, 0xb9, 0x31, 0xe9, 0xb3, 0xd8, 0x9d, 0x29, 0x3a, 0x12, 0x7f, 0xf8, 0xc7, 0x6e, 0x4c, 0xf8, 0x30, 0x7d, 0xf7, + 0x4f, 0xed, 0x27, 0x5b, 0x72, 0x48, 0x14, 0xff, 0x05, 0xff, 0x87, 0x72, 0x2c, 0x8e, 0x8c, 0xf3, 0xa6, 0x4b, 0xc9, + 0x77, 0xbb, 0x2f, 0x25, 0xbf, 0x59, 0xef, 0x73, 0x29, 0xf9, 0xee, 0xb3, 0x5f, 0x4a, 0x9e, 0x97, 0x7d, 0x6b, 0xde, + 0x95, 0x53, 0x41, 0x7d, 0xb7, 0x29, 0xab, 0x1c, 0x3c, 0x57, 0xbb, 0xbc, 0x58, 0x5f, 0x41, 0x68, 0xbf, 0x77, 0xe1, + 0xe0, 0x9b, 0x75, 0xc1, 0xe0, 0x33, 0x04, 0x1c, 0xfb, 0x2e, 0x24, 0x1c, 0xfb, 0xc3, 0x7a, 0x00, 0x56, 0x66, 0x9c, + 0xcd, 0xf1, 0xa6, 0xe6, 0xc2, 0xf5, 0x67, 0x19, 0x8b, 0x04, 0x25, 0x7d, 0x2c, 0x06, 0xc7, 0x35, 0x20, 0xcf, 0x20, + 0xc9, 0xac, 0x97, 0x41, 0x0c, 0x16, 0xc1, 0x60, 0xc9, 0x31, 0x8b, 0xd2, 0x52, 0x63, 0x4b, 0x04, 0x43, 0xbc, 0xe6, + 0x5e, 0x50, 0x8d, 0xef, 0xd1, 0x00, 0xb8, 0xbe, 0x77, 0xa7, 0xda, 0xaf, 0x02, 0x96, 0x75, 0xc2, 0x40, 0x1a, 0xb8, + 0xfd, 0xba, 0xf7, 0x45, 0x33, 0xdc, 0x92, 0xe1, 0x75, 0xf3, 0x48, 0x61, 0x24, 0xe5, 0xf6, 0x4e, 0xd1, 0x8c, 0x77, + 0xd7, 0x34, 0x6b, 0x3e, 0x5f, 0x68, 0xbe, 0xc5, 0x86, 0x38, 0xeb, 0xb8, 0x0c, 0xaa, 0x52, 0x22, 0xe3, 0x5a, 0x80, + 0xe4, 0x02, 0x6a, 0x6e, 0x68, 0x9c, 0x73, 0xaa, 0xb6, 0x82, 0xfc, 0x8e, 0x2d, 0xbd, 0x2b, 0xf4, 0x29, 0x1b, 0x27, + 0x3f, 0xdb, 0xa0, 0x5c, 0xe1, 0xfd, 0x0a, 0x9c, 0x28, 0xe7, 0x78, 0xc6, 0xa1, 0x0c, 0xe7, 0x8d, 0xd4, 0x2f, 0x69, + 0x23, 0xd2, 0x85, 0xb3, 0xa9, 0xf2, 0xa2, 0x8d, 0x6e, 0x09, 0x0e, 0x5b, 0x0a, 0x2e, 0x08, 0x3f, 0x4f, 0x4e, 0x00, + 0x29, 0x39, 0x6a, 0xa0, 0x9f, 0xc3, 0xb6, 0xce, 0x44, 0xbd, 0xc7, 0xb0, 0x89, 0x79, 0xd0, 0x65, 0x45, 0x8e, 0x36, + 0xb3, 0x99, 0xf9, 0xa1, 0x9b, 0xf4, 0x90, 0x4d, 0x93, 0x58, 0xde, 0x16, 0x7a, 0x2c, 0xf4, 0xb7, 0x18, 0xd3, 0xc9, + 0x1d, 0xf3, 0x4e, 0xd0, 0xf3, 0x61, 0x9b, 0xfd, 0x5d, 0xe6, 0x70, 0xb6, 0x29, 0x98, 0xa3, 0x38, 0x9d, 0x63, 0xc3, + 0x39, 0x32, 0xac, 0xe3, 0x8e, 0x9e, 0x8a, 0x03, 0x27, 0x77, 0x59, 0x00, 0x08, 0x38, 0x40, 0x64, 0xc3, 0xf4, 0x02, + 0x2f, 0xf1, 0x5c, 0x3f, 0x05, 0x7e, 0xb8, 0x28, 0xa4, 0xfc, 0x6b, 0x1d, 0x27, 0x30, 0x47, 0xc1, 0xf4, 0xa2, 0xf3, + 0x87, 0x39, 0x66, 0xc9, 0x2d, 0x63, 0x41, 0x83, 0x61, 0x4c, 0xd9, 0x97, 0xe4, 0xf7, 0xb3, 0xac, 0x4f, 0xc9, 0x6a, + 0x6d, 0x9c, 0x04, 0x7c, 0x7f, 0x08, 0xc7, 0x87, 0x74, 0x64, 0xfc, 0xda, 0x84, 0x70, 0xff, 0xb5, 0x1b, 0xe1, 0x26, + 0x6c, 0x1f, 0x84, 0xfb, 0xaf, 0xcf, 0x8e, 0x70, 0x7f, 0x95, 0x11, 0x6e, 0xc1, 0x7f, 0x30, 0xbf, 0x61, 0x7a, 0x8f, + 0xcf, 0x1a, 0x24, 0x51, 0x79, 0xae, 0x1e, 0x10, 0x03, 0x0f, 0xf9, 0x25, 0x44, 0x14, 0xaf, 0x97, 0x85, 0x64, 0x9d, + 0xa8, 0x00, 0xc5, 0x04, 0x1d, 0x94, 0x18, 0xd0, 0x03, 0x57, 0xb7, 0x2c, 0x39, 0x20, 0xbb, 0x55, 0xce, 0x82, 0xc4, + 0xb7, 0xde, 0x71, 0x39, 0x12, 0x2e, 0x74, 0xbf, 0x09, 0xa3, 0xa5, 0x8b, 0xd1, 0x5f, 0x55, 0x4c, 0xf2, 0x0d, 0x0f, + 0x36, 0x38, 0xe3, 0x4e, 0xc2, 0x60, 0x9a, 0xdd, 0x4a, 0xb2, 0xc1, 0x25, 0x71, 0xdc, 0xea, 0x3d, 0x73, 0x23, 0xd5, + 0xa0, 0xd7, 0xb0, 0xb8, 0xcf, 0xda, 0xf6, 0xb3, 0xd6, 0xe1, 0xb3, 0x23, 0x1b, 0xfe, 0x77, 0x58, 0x3b, 0x35, 0x78, + 0xc5, 0x65, 0x18, 0x40, 0x9e, 0x41, 0x51, 0xb3, 0xa9, 0xda, 0x2d, 0x63, 0x1f, 0xf2, 0x5a, 0xc7, 0xf5, 0x95, 0xa6, + 0xee, 0x7d, 0x5e, 0xa7, 0xb6, 0xc6, 0x22, 0x5c, 0x4b, 0xc3, 0xaa, 0x19, 0x8d, 0x17, 0xac, 0x41, 0xcf, 0x2e, 0xd5, + 0x90, 0x5f, 0xf3, 0xe9, 0xe6, 0xf3, 0x62, 0xed, 0xf4, 0x2a, 0x4f, 0x66, 0x2a, 0x92, 0x2a, 0xee, 0x84, 0x20, 0xbf, + 0xa2, 0xb4, 0x31, 0xde, 0x37, 0x66, 0x9c, 0x80, 0x68, 0xdf, 0x59, 0x0a, 0x4a, 0x97, 0x16, 0x28, 0x89, 0xd6, 0xc1, + 0x44, 0xc3, 0x9f, 0xee, 0x38, 0xd6, 0xbc, 0x83, 0xc8, 0xe2, 0x1f, 0xd6, 0x71, 0xd5, 0xdc, 0xa1, 0x9d, 0x67, 0x7e, + 0x8b, 0xc5, 0xaa, 0xb8, 0xcf, 0x12, 0x23, 0xc2, 0x7b, 0x6c, 0x5a, 0x5a, 0x73, 0xe0, 0x3e, 0xcb, 0x1a, 0x3e, 0x4b, + 0x8c, 0xe0, 0x39, 0xdc, 0x7d, 0x0e, 0xec, 0xa7, 0x4f, 0xa9, 0x16, 0x64, 0x51, 0xa7, 0x69, 0x9d, 0x4e, 0xf2, 0xa0, + 0xa1, 0x8a, 0x3b, 0x0f, 0x29, 0x6e, 0x68, 0x6f, 0x62, 0x84, 0xcf, 0x9f, 0x0f, 0x07, 0x8e, 0x8e, 0x59, 0x45, 0x45, + 0x76, 0x70, 0x9e, 0xb0, 0xf6, 0x7c, 0x3f, 0x43, 0x23, 0xbd, 0xd6, 0x95, 0x76, 0x05, 0x32, 0x93, 0x2d, 0xdc, 0x11, + 0x38, 0xf6, 0x82, 0x04, 0x72, 0x64, 0x50, 0xe0, 0x0a, 0x83, 0x1f, 0x51, 0x27, 0x93, 0xba, 0xda, 0x96, 0x6d, 0xd9, + 0x6a, 0xd6, 0x70, 0xe6, 0xcd, 0x07, 0x9b, 0x30, 0x71, 0x21, 0x15, 0xa7, 0x1f, 0xce, 0xc1, 0x8f, 0x2e, 0xf1, 0x12, + 0x1f, 0xf2, 0x3a, 0x82, 0x43, 0xdd, 0x92, 0xe4, 0xf2, 0x94, 0x7b, 0x37, 0xb8, 0xd1, 0x07, 0xcc, 0xed, 0x2d, 0x5c, + 0x71, 0x31, 0x8e, 0xdd, 0xf7, 0x40, 0x0c, 0x35, 0x55, 0x03, 0xdd, 0x00, 0x8b, 0x62, 0x53, 0xf6, 0x16, 0xea, 0x29, + 0xd0, 0x46, 0x57, 0xf9, 0x24, 0x66, 0x91, 0xbb, 0x84, 0x1c, 0x48, 0x9b, 0xd4, 0xe0, 0x98, 0x56, 0xe5, 0xa8, 0x56, + 0x71, 0x5e, 0x1c, 0x19, 0x4a, 0xcb, 0x31, 0x14, 0x1b, 0xd0, 0xad, 0x9a, 0x1a, 0x9b, 0xf4, 0xaa, 0xbf, 0xcb, 0xe0, + 0x81, 0xf0, 0xcb, 0x63, 0x9a, 0x07, 0x99, 0x3a, 0xf0, 0xab, 0xa4, 0x84, 0xe2, 0x17, 0x6b, 0x52, 0x66, 0x13, 0x8f, + 0x2e, 0x3d, 0x2f, 0xd8, 0x5d, 0xa2, 0x63, 0xde, 0x43, 0x5e, 0xc5, 0xd3, 0x37, 0xe8, 0x30, 0xec, 0x05, 0x8a, 0xf7, + 0xf1, 0xa3, 0xe6, 0x81, 0x33, 0xd3, 0x40, 0x82, 0x0f, 0x3c, 0xeb, 0x05, 0x80, 0x79, 0xf9, 0x35, 0x3d, 0x02, 0x0b, + 0x3c, 0x0d, 0xe1, 0xdf, 0xbc, 0x58, 0xfc, 0xe0, 0x66, 0x12, 0x96, 0xef, 0x06, 0x73, 0x40, 0x69, 0x6e, 0x30, 0xaf, + 0x98, 0x63, 0x91, 0xcf, 0x73, 0xa9, 0x34, 0xef, 0x2a, 0x37, 0x95, 0x8a, 0x5f, 0xde, 0x5f, 0x50, 0x5e, 0x57, 0x4d, + 0x05, 0x2a, 0x87, 0x2e, 0xba, 0xf9, 0x4d, 0xee, 0xf3, 0xc1, 0x97, 0x27, 0x4b, 0x96, 0xb8, 0x74, 0x0d, 0x04, 0xc2, + 0x2f, 0xb0, 0x03, 0x0a, 0x27, 0x34, 0x3c, 0x36, 0xd4, 0x60, 0xca, 0x6e, 0xbc, 0x09, 0x97, 0x4b, 0x0d, 0x85, 0xd3, + 0x29, 0x13, 0x2d, 0x3e, 0x07, 0x8e, 0x41, 0x0e, 0x07, 0x13, 0x17, 0x43, 0x1d, 0x0f, 0x82, 0x50, 0x1d, 0x7e, 0x99, + 0xf9, 0x66, 0x36, 0x2d, 0x02, 0x24, 0x57, 0xbf, 0x8c, 0x98, 0xff, 0xef, 0xc1, 0x97, 0x40, 0xb8, 0xbf, 0xbc, 0x52, + 0xf5, 0x7e, 0x62, 0x2d, 0x22, 0x36, 0x1b, 0x7c, 0x59, 0x93, 0x64, 0x1c, 0xc5, 0x7b, 0x1a, 0x8b, 0xda, 0x6e, 0xe5, + 0x21, 0xe7, 0xda, 0x7b, 0x09, 0xf5, 0x43, 0x2e, 0xad, 0x83, 0x04, 0xb8, 0x29, 0xc8, 0xd8, 0x4e, 0x1f, 0xe5, 0xe7, + 0xb1, 0xef, 0x4e, 0x3e, 0xf4, 0xe9, 0x4d, 0xe1, 0xc1, 0x04, 0x6a, 0x3d, 0x71, 0x57, 0x3d, 0x24, 0xaf, 0x72, 0x21, + 0x78, 0x4f, 0x53, 0x69, 0xc6, 0xd9, 0xd5, 0xee, 0x65, 0xdc, 0xca, 0x1b, 0xfc, 0x32, 0x7e, 0xea, 0x76, 0xe1, 0x25, + 0x4c, 0x7c, 0x0a, 0x1f, 0xd2, 0x54, 0x08, 0xea, 0x24, 0xa2, 0xa2, 0x60, 0x6d, 0xb5, 0x15, 0xa7, 0xfb, 0x6d, 0xe7, + 0xc6, 0xb1, 0x17, 0x2d, 0xc7, 0xea, 0xfe, 0xec, 0x74, 0x17, 0x6d, 0xeb, 0xd8, 0x37, 0xdb, 0xd6, 0x31, 0xfc, 0xf9, + 0xf9, 0xd8, 0xea, 0x2e, 0xcc, 0x96, 0x75, 0xf8, 0xb3, 0xd3, 0xf2, 0xcd, 0xae, 0x75, 0x0c, 0x7f, 0xce, 0xa9, 0x15, + 0x08, 0x40, 0x24, 0xef, 0x7c, 0x59, 0xc0, 0x02, 0xd2, 0xef, 0xec, 0x4e, 0xd6, 0x28, 0x90, 0xb7, 0x9a, 0x7b, 0x5d, + 0x40, 0x19, 0x94, 0xfa, 0x07, 0x4d, 0x11, 0xfa, 0x5a, 0x30, 0x60, 0x94, 0xec, 0x47, 0x98, 0xb7, 0x09, 0x3f, 0x74, + 0x91, 0x5f, 0xa5, 0xf6, 0x18, 0xf1, 0x36, 0xf5, 0x39, 0x45, 0x44, 0x22, 0x58, 0xba, 0x08, 0xfe, 0x69, 0x85, 0xa1, + 0xf1, 0x44, 0xfa, 0x2c, 0x09, 0x2b, 0xe5, 0xc9, 0xc8, 0xd3, 0xdd, 0x03, 0x47, 0x6f, 0x7e, 0x96, 0x25, 0xc3, 0xfc, + 0xac, 0x7d, 0x4b, 0x59, 0xca, 0x3e, 0xa9, 0x1f, 0xcc, 0xce, 0x94, 0x27, 0x56, 0x82, 0x88, 0xe2, 0x53, 0x2f, 0xca, + 0x86, 0x27, 0xa1, 0x68, 0xa7, 0x3e, 0x19, 0x8b, 0x0e, 0x59, 0x03, 0xcf, 0x80, 0x4b, 0xbe, 0x71, 0x7d, 0xc9, 0x90, + 0x4d, 0x6a, 0xf9, 0x28, 0xc3, 0xfc, 0x4f, 0x9f, 0xe6, 0x83, 0x33, 0x4b, 0xe3, 0x3e, 0x71, 0x3a, 0x40, 0x76, 0x3b, + 0xac, 0xbd, 0xd5, 0xa6, 0x72, 0x77, 0x2c, 0xfa, 0x3c, 0x08, 0xb5, 0xb0, 0x9b, 0x12, 0x16, 0x1b, 0x8d, 0x86, 0x9d, + 0x15, 0x7b, 0x0d, 0x88, 0xe2, 0x5f, 0x12, 0x75, 0x54, 0xbd, 0x1f, 0x08, 0xf3, 0x83, 0x60, 0x4b, 0xfc, 0x7d, 0x2e, + 0x8b, 0xa9, 0x00, 0x9a, 0x2d, 0xf3, 0xd8, 0xe1, 0x20, 0xfe, 0x67, 0x4f, 0x02, 0x9d, 0x35, 0xc1, 0x5e, 0xa2, 0x74, + 0x5a, 0x0b, 0xce, 0x7b, 0x19, 0x5d, 0x25, 0x82, 0xca, 0xe2, 0x53, 0x15, 0x8a, 0x20, 0x9d, 0x2c, 0x66, 0x90, 0xce, + 0x8c, 0x45, 0x33, 0x6a, 0x91, 0x17, 0x18, 0x1e, 0x26, 0x33, 0x11, 0x8e, 0xa3, 0xfa, 0xd3, 0xa7, 0x8d, 0x44, 0x88, + 0x8c, 0x73, 0x62, 0x96, 0x64, 0x39, 0x2e, 0x55, 0x19, 0xbf, 0xa9, 0x32, 0x8a, 0xc9, 0xfa, 0x45, 0xac, 0x21, 0x6c, + 0x5c, 0x69, 0xef, 0xe1, 0xcf, 0x31, 0x73, 0x13, 0x8b, 0x5f, 0x96, 0x6a, 0x12, 0x71, 0x37, 0x1c, 0xd6, 0x06, 0xeb, + 0x56, 0x1e, 0x41, 0x93, 0x47, 0xa8, 0x7d, 0xb2, 0x79, 0xb9, 0xe6, 0x51, 0x1d, 0xa0, 0x8f, 0x8f, 0x76, 0x1e, 0x80, + 0xec, 0x6d, 0xe2, 0x52, 0x60, 0x18, 0x99, 0xe4, 0x86, 0x89, 0x2b, 0xd2, 0x36, 0x02, 0x5f, 0xde, 0xaf, 0x35, 0xbf, + 0x90, 0x22, 0x3f, 0x0c, 0xdf, 0x5e, 0x7c, 0xad, 0xf0, 0xfd, 0x4f, 0xd6, 0x02, 0x28, 0xc8, 0x50, 0x6a, 0x9f, 0x01, + 0xa5, 0xf6, 0x51, 0x78, 0x6e, 0x29, 0xc8, 0x77, 0x93, 0x1e, 0x10, 0x04, 0x51, 0x01, 0x4d, 0x36, 0x14, 0xcb, 0xb5, + 0x9f, 0x78, 0x2b, 0x37, 0x4a, 0x0e, 0x30, 0xaf, 0x0f, 0x20, 0x39, 0xb5, 0x29, 0x1e, 0x04, 0x99, 0x61, 0x88, 0xc0, + 0xad, 0x49, 0x20, 0xec, 0x30, 0x66, 0x9e, 0x9f, 0x99, 0x61, 0x88, 0x0f, 0xb8, 0x93, 0x09, 0x5b, 0x25, 0x83, 0x42, + 0xfe, 0xa0, 0x70, 0x92, 0xb0, 0xc4, 0x8c, 0x93, 0x88, 0xb9, 0x4b, 0x35, 0x0b, 0x10, 0x5e, 0xed, 0x2f, 0x5e, 0x8f, + 0x97, 0x5e, 0x92, 0x45, 0xd8, 0xa5, 0x09, 0x82, 0x41, 0x04, 0x0c, 0x71, 0x38, 0x4a, 0x39, 0x08, 0xcf, 0xc3, 0x79, + 0x69, 0x47, 0xe5, 0x9c, 0xcb, 0x29, 0xc6, 0x6f, 0x27, 0x49, 0x06, 0xb4, 0xc5, 0x93, 0xd0, 0xbf, 0xe6, 0x31, 0x2c, + 0xb2, 0x40, 0xc0, 0xea, 0xf0, 0x84, 0x8b, 0xb7, 0x0a, 0x86, 0x6f, 0x51, 0x3b, 0x36, 0x44, 0xa8, 0x6f, 0x8a, 0x6e, + 0x71, 0xc0, 0x2b, 0x03, 0x69, 0xa2, 0x9e, 0x31, 0xc9, 0x08, 0x8d, 0xe5, 0x02, 0x18, 0xa1, 0x82, 0xc1, 0xcc, 0xc2, + 0x19, 0x66, 0xee, 0x94, 0x38, 0x2a, 0xe4, 0x95, 0x3e, 0x7e, 0x7c, 0x35, 0xfa, 0xed, 0x3f, 0x90, 0x09, 0x65, 0xe1, + 0x88, 0x98, 0x12, 0x97, 0x72, 0x2d, 0xce, 0x7d, 0x1a, 0x23, 0x34, 0x96, 0x62, 0x53, 0x11, 0xa2, 0x47, 0x6c, 0xad, + 0x74, 0x74, 0x25, 0x42, 0x34, 0x42, 0x92, 0x24, 0x5d, 0x44, 0xbe, 0x18, 0xc1, 0xf2, 0x8e, 0x44, 0x4c, 0x14, 0xe5, + 0x97, 0xbb, 0x97, 0xc7, 0x4a, 0x1e, 0xc3, 0xa8, 0xce, 0xa2, 0x87, 0xf6, 0xd0, 0xf0, 0xc4, 0x55, 0x90, 0x69, 0x41, + 0xf6, 0x23, 0xee, 0x1d, 0xc0, 0x34, 0x17, 0xe1, 0x92, 0x59, 0x5e, 0x78, 0x70, 0xcb, 0xc6, 0xa6, 0xbb, 0xf2, 0xc8, + 0x2e, 0x07, 0xf5, 0x6e, 0x0a, 0x71, 0x7e, 0x99, 0xb9, 0x0b, 0xf1, 0xd7, 0x69, 0x0e, 0xca, 0xb0, 0x18, 0x93, 0xb3, + 0xd3, 0xca, 0xef, 0x01, 0x21, 0x7e, 0x81, 0x04, 0xc7, 0x70, 0x78, 0x72, 0xe0, 0x0e, 0x8b, 0x41, 0x81, 0x2d, 0x91, + 0xbd, 0xa6, 0x48, 0x04, 0x4e, 0x29, 0xb6, 0xaf, 0x08, 0xe3, 0x9b, 0x3f, 0x98, 0xe1, 0x6c, 0x26, 0x07, 0xf2, 0xb5, + 0x8a, 0xc3, 0xcb, 0x80, 0x96, 0x6f, 0xe9, 0x70, 0x45, 0x5f, 0xaa, 0x7e, 0x22, 0xfb, 0xa9, 0xf6, 0x30, 0x82, 0x37, + 0xcc, 0x19, 0x8e, 0x7b, 0x25, 0x20, 0x70, 0x06, 0xb1, 0xc7, 0x54, 0x89, 0xe3, 0x91, 0x72, 0xfa, 0x89, 0x06, 0xce, + 0xe5, 0xd1, 0x60, 0x40, 0x68, 0xae, 0x8c, 0xed, 0x00, 0x88, 0x35, 0x99, 0x7c, 0x60, 0xb2, 0x09, 0x34, 0x34, 0xc9, + 0x5d, 0x16, 0x1b, 0x95, 0xa7, 0x53, 0x1d, 0xe3, 0x81, 0x2b, 0xb6, 0x5f, 0x61, 0x83, 0xc2, 0xc6, 0xe3, 0xeb, 0x0e, + 0xf8, 0x5d, 0xf4, 0x53, 0x42, 0xf3, 0xca, 0x57, 0x84, 0xd1, 0x4d, 0xdf, 0xbd, 0x0f, 0x25, 0x33, 0x26, 0x1e, 0xd1, + 0xe4, 0x1c, 0x4b, 0x2f, 0x84, 0x27, 0x71, 0xe5, 0xa0, 0x65, 0x09, 0x51, 0xaa, 0x87, 0x4d, 0x4e, 0x62, 0xb2, 0xeb, + 0xac, 0xc9, 0x75, 0x8b, 0x93, 0x41, 0xe4, 0x99, 0xe6, 0xe7, 0xb0, 0xf0, 0x12, 0xd1, 0x42, 0x7a, 0x72, 0x00, 0xf3, + 0x83, 0x28, 0x2c, 0x05, 0xc6, 0xc9, 0xd3, 0x21, 0xd4, 0x8b, 0x1b, 0x93, 0x29, 0xd6, 0xd9, 0x54, 0xf0, 0x7c, 0x28, + 0x58, 0x4a, 0xd9, 0xfd, 0xa4, 0x2a, 0x55, 0x5e, 0xc6, 0xae, 0x67, 0x02, 0x77, 0x67, 0x0f, 0xfa, 0x61, 0x8d, 0xa9, + 0x83, 0xd2, 0x7e, 0xc2, 0x44, 0x90, 0x83, 0xf3, 0xa4, 0x21, 0x0e, 0x42, 0x53, 0x15, 0xe2, 0x67, 0xb7, 0x54, 0xc8, + 0xf7, 0xf1, 0xb6, 0x5a, 0x39, 0xe7, 0x94, 0x55, 0x5b, 0xb9, 0x9a, 0xfa, 0x18, 0x77, 0x7c, 0xa5, 0x36, 0x96, 0x42, + 0xbd, 0xf3, 0x64, 0x00, 0x55, 0x85, 0x2e, 0xde, 0x5d, 0xad, 0xa8, 0xb2, 0xde, 0x3f, 0x39, 0x20, 0xb1, 0x74, 0x48, + 0x3b, 0x6c, 0x78, 0x02, 0xa6, 0xdc, 0xb4, 0xe8, 0xee, 0x6a, 0xc5, 0x97, 0x94, 0x7e, 0xd1, 0x9b, 0x83, 0x45, 0xb2, + 0xf4, 0x87, 0xff, 0x07, 0x52, 0xaf, 0x09, 0x6c, 0x30, 0x6a, 0x03, 0x00}; } // namespace web_server } // namespace esphome diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 63c1d5d4fd..870932d266 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -235,7 +235,7 @@ std::string WebServer::get_config_json() { } void WebServer::setup() { - ESP_LOGCONFIG(TAG, "Setting up web server..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->setup_controller(this->include_internal_); this->base_->init(); @@ -282,8 +282,10 @@ void WebServer::loop() { this->events_.loop(); } void WebServer::dump_config() { - ESP_LOGCONFIG(TAG, "Web Server:"); - ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->base_->get_port()); + ESP_LOGCONFIG(TAG, + "Web Server:\n" + " Address: %s:%u", + network::get_use_address().c_str(), this->base_->get_port()); } float WebServer::get_setup_priority() const { return setup_priority::WIFI - 1.0f; } diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index 7c09022f27..2835585387 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -1,8 +1,8 @@ #include "web_server_base.h" #ifdef USE_NETWORK -#include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_ARDUINO #include diff --git a/esphome/components/web_server_idf/__init__.py b/esphome/components/web_server_idf/__init__.py index 73c51f8cb5..506e1c5c13 100644 --- a/esphome/components/web_server_idf/__init__.py +++ b/esphome/components/web_server_idf/__init__.py @@ -8,8 +8,6 @@ CONFIG_SCHEMA = cv.All( cv.only_with_esp_idf, ) -AUTO_LOAD = ["web_server"] - async def to_code(config): # Increase the maximum supported size of headers section in HTTP request packet to be processed by the server diff --git a/esphome/components/web_server_idf/utils.cpp b/esphome/components/web_server_idf/utils.cpp index 6299937ce1..349acce50d 100644 --- a/esphome/components/web_server_idf/utils.cpp +++ b/esphome/components/web_server_idf/utils.cpp @@ -1,7 +1,7 @@ #ifdef USE_ESP_IDF #include -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "http_parser.h" #include "utils.h" diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index 428bd262e8..90fdf720cd 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -2,17 +2,19 @@ #include -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esp_tls_crypto.h" #include "utils.h" +#include "web_server_idf.h" + +#ifdef USE_WEBSERVER #include "esphome/components/web_server/web_server.h" #include "esphome/components/web_server/list_entities.h" - -#include "web_server_idf.h" +#endif // USE_WEBSERVER namespace esphome { namespace web_server_idf { @@ -273,6 +275,7 @@ void AsyncResponseStream::printf(const char *fmt, ...) { this->print(str); } +#ifdef USE_WEBSERVER AsyncEventSource::~AsyncEventSource() { for (auto *ses : this->sessions_) { delete ses; // NOLINT(cppcoreguidelines-owning-memory) @@ -511,6 +514,7 @@ void AsyncEventSourceResponse::deferrable_send_state(void *source, const char *e } } } +#endif } // namespace web_server_idf } // namespace esphome diff --git a/esphome/components/web_server_idf/web_server_idf.h b/esphome/components/web_server_idf/web_server_idf.h index 13a3ef168d..d883c0ca9b 100644 --- a/esphome/components/web_server_idf/web_server_idf.h +++ b/esphome/components/web_server_idf/web_server_idf.h @@ -1,6 +1,7 @@ #pragma once #ifdef USE_ESP_IDF +#include "esphome/core/defines.h" #include #include @@ -12,10 +13,12 @@ #include namespace esphome { +#ifdef USE_WEBSERVER namespace web_server { class WebServer; class ListEntitiesIterator; }; // namespace web_server +#endif namespace web_server_idf { #define F(string_literal) (string_literal) @@ -220,6 +223,7 @@ class AsyncWebHandler { virtual bool isRequestHandlerTrivial() { return true; } }; +#ifdef USE_WEBSERVER class AsyncEventSource; class AsyncEventSourceResponse; @@ -307,10 +311,13 @@ class AsyncEventSource : public AsyncWebHandler { connect_handler_t on_connect_{}; esphome::web_server::WebServer *web_server_; }; +#endif // USE_WEBSERVER class DefaultHeaders { friend class AsyncWebServerRequest; +#ifdef USE_WEBSERVER friend class AsyncEventSourceResponse; +#endif public: // NOLINTNEXTLINE(readability-identifier-naming) diff --git a/esphome/components/weikai/__init__.py b/esphome/components/weikai/__init__.py index 4c8f7e700d..796423438e 100644 --- a/esphome/components/weikai/__init__.py +++ b/esphome/components/weikai/__init__.py @@ -16,6 +16,7 @@ CODEOWNERS = ["@DrCoolZic"] AUTO_LOAD = ["uart"] MULTI_CONF = True +CONF_DATA_BITS = "data_bits" CONF_STOP_BITS = "stop_bits" CONF_PARITY = "parity" CONF_CRYSTAL = "crystal" @@ -60,6 +61,7 @@ WKBASE_SCHEMA = cv.Schema( cv.Required(CONF_ID): cv.declare_id(WeikaiChannel), cv.Optional(CONF_CHANNEL, default=0): cv.int_range(min=0, max=3), cv.Required(CONF_BAUD_RATE): cv.int_range(min=1), + cv.Optional(CONF_DATA_BITS, default=8): cv.one_of(8, int=True), cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True), cv.Optional(CONF_PARITY, default="NONE"): cv.enum( uart.UART_PARITY_OPTIONS, upper=True diff --git a/esphome/components/weikai/weikai.cpp b/esphome/components/weikai/weikai.cpp index 19aa09e20d..ebe987cc65 100644 --- a/esphome/components/weikai/weikai.cpp +++ b/esphome/components/weikai/weikai.cpp @@ -102,7 +102,7 @@ WeikaiRegister &WeikaiRegister::operator|=(uint8_t value) { // The WeikaiComponent methods /////////////////////////////////////////////////////////////////////////////// void WeikaiComponent::loop() { - if ((this->component_state_ & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP) + if (!this->is_in_loop_state()) return; // If there are some bytes in the receive FIFO we transfers them to the ring buffers @@ -112,7 +112,7 @@ void WeikaiComponent::loop() { transferred += child->xfer_fifo_to_buffer_(); } if (transferred > 0) { - ESP_LOGV(TAG, "we transferred %d bytes from fifo to buffer...", transferred); + ESP_LOGV(TAG, "transferred %d bytes from fifo to buffer", transferred); } #ifdef TEST_COMPONENT @@ -121,8 +121,8 @@ void WeikaiComponent::loop() { uint32_t time = 0; if (test_mode_ == 1) { // test component in loopback - ESP_LOGI(TAG, "Component loop %" PRIu32 " for %s : %" PRIu32 " ms since last call ...", loop_count++, - this->get_name(), millis() - loop_time); + ESP_LOGI(TAG, "Component loop %" PRIu32 " for %s : %" PRIu32 " ms since last call", loop_count++, this->get_name(), + millis() - loop_time); loop_time = millis(); char message[64]; elapsed_ms(time); // set time to now @@ -134,14 +134,14 @@ void WeikaiComponent::loop() { uint32_t const start_time = millis(); while (children_[i]->tx_fifo_is_not_empty_()) { // wait until buffer empty if (millis() - start_time > 1500) { - ESP_LOGE(TAG, "timeout while flushing - %d bytes left in buffer...", children_[i]->tx_in_fifo_()); + ESP_LOGE(TAG, "timeout while flushing - %d bytes left in buffer", children_[i]->tx_in_fifo_()); break; } yield(); // reschedule our thread to avoid blocking } bool status = children_[i]->uart_receive_test_(message); - ESP_LOGI(TAG, "Test %s => send/received %u bytes %s - execution time %" PRIu32 " ms...", message, - RING_BUFFER_SIZE, status ? "correctly" : "with error", elapsed_ms(time)); + ESP_LOGI(TAG, "Test %s => send/received %u bytes %s - execution time %" PRIu32 " ms", message, RING_BUFFER_SIZE, + status ? "correctly" : "with error", elapsed_ms(time)); } } @@ -255,10 +255,10 @@ std::string WeikaiGPIOPin::dump_summary() const { // The WeikaiChannel methods /////////////////////////////////////////////////////////////////////////////// void WeikaiChannel::setup_channel() { - ESP_LOGCONFIG(TAG, " Setting up UART %s:%s ...", this->parent_->get_name(), this->get_channel_name()); + ESP_LOGCONFIG(TAG, " Setting up UART %s:%s", this->parent_->get_name(), this->get_channel_name()); // we enable transmit and receive on this channel if (this->check_channel_down()) { - ESP_LOGCONFIG(TAG, " Error channel %s not working...", this->get_channel_name()); + ESP_LOGCONFIG(TAG, " Error channel %s not working", this->get_channel_name()); } this->reset_fifo_(); this->receive_buffer_.clear(); @@ -267,11 +267,13 @@ void WeikaiChannel::setup_channel() { } void WeikaiChannel::dump_channel() { - ESP_LOGCONFIG(TAG, " UART %s ...", this->get_channel_name()); - ESP_LOGCONFIG(TAG, " Baud rate: %" PRIu32 " Bd", this->baud_rate_); - ESP_LOGCONFIG(TAG, " Data bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", p2s(this->parity_)); + ESP_LOGCONFIG(TAG, + " UART %s\n" + " Baud rate: %" PRIu32 " Bd\n" + " Data bits: %u\n" + " Stop bits: %u\n" + " Parity: %s", + this->get_channel_name(), this->baud_rate_, this->data_bits_, this->stop_bits_, p2s(this->parity_)); } void WeikaiChannel::reset_fifo_() { @@ -407,7 +409,7 @@ bool WeikaiChannel::read_array(uint8_t *buffer, size_t length) { bool status = true; auto available = this->receive_buffer_.count(); if (length > available) { - ESP_LOGW(TAG, "read_array: buffer underflow requested %d bytes only %d bytes available...", length, available); + ESP_LOGW(TAG, "read_array: buffer underflow requested %d bytes only %d bytes available", length, available); length = available; status = false; } @@ -422,7 +424,7 @@ bool WeikaiChannel::read_array(uint8_t *buffer, size_t length) { void WeikaiChannel::write_array(const uint8_t *buffer, size_t length) { if (length > XFER_MAX_SIZE) { - ESP_LOGE(TAG, "Write_array: invalid call - requested %d bytes but max size %d ...", length, XFER_MAX_SIZE); + ESP_LOGE(TAG, "Write_array: invalid call - requested %d bytes but max size %d", length, XFER_MAX_SIZE); length = XFER_MAX_SIZE; } this->reg(0).write_fifo(const_cast(buffer), length); @@ -432,7 +434,7 @@ void WeikaiChannel::flush() { uint32_t const start_time = millis(); while (this->tx_fifo_is_not_empty_()) { // wait until buffer empty if (millis() - start_time > 200) { - ESP_LOGW(TAG, "WARNING flush timeout - still %d bytes not sent after 200 ms...", this->tx_in_fifo_()); + ESP_LOGW(TAG, "WARNING flush timeout - still %d bytes not sent after 200 ms", this->tx_in_fifo_()); return; } yield(); // reschedule our thread to avoid blocking @@ -509,7 +511,7 @@ void WeikaiChannel::uart_send_test_(char *message) { this->flush(); to_send -= XFER_MAX_SIZE; } - ESP_LOGV(TAG, "%s => sent %d bytes - exec time %d µs ...", message, RING_BUFFER_SIZE, micros() - start_exec); + ESP_LOGV(TAG, "%s => sent %d bytes - exec time %d µs", message, RING_BUFFER_SIZE, micros() - start_exec); } /// @brief test read_array method @@ -526,7 +528,7 @@ bool WeikaiChannel::uart_receive_test_(char *message) { while (XFER_MAX_SIZE > this->available()) { this->xfer_fifo_to_buffer_(); if (millis() - start_time > 1500) { - ESP_LOGE(TAG, "uart_receive_test_() timeout: only %d bytes received...", this->available()); + ESP_LOGE(TAG, "uart_receive_test_() timeout: only %d bytes received", this->available()); break; } yield(); // reschedule our thread to avoid blocking @@ -538,20 +540,20 @@ bool WeikaiChannel::uart_receive_test_(char *message) { uint8_t peek_value = 0; this->peek_byte(&peek_value); if (peek_value != 0) { - ESP_LOGE(TAG, "Peek first byte value error..."); + ESP_LOGE(TAG, "Peek first byte value error"); status = false; } for (size_t i = 0; i < RING_BUFFER_SIZE; i++) { if (buffer[i] != i % XFER_MAX_SIZE) { - ESP_LOGE(TAG, "Read buffer contains error...b=%x i=%x", buffer[i], i % XFER_MAX_SIZE); + ESP_LOGE(TAG, "Read buffer contains error b=%x i=%x", buffer[i], i % XFER_MAX_SIZE); print_buffer(buffer); status = false; break; } } - ESP_LOGV(TAG, "%s => received %d bytes status %s - exec time %d µs ...", message, received, status ? "OK" : "ERROR", + ESP_LOGV(TAG, "%s => received %d bytes status %s - exec time %d µs", message, received, status ? "OK" : "ERROR", micros() - start_exec); return status; } diff --git a/esphome/components/weikai_i2c/weikai_i2c.cpp b/esphome/components/weikai_i2c/weikai_i2c.cpp index 9d0c9446ec..32e7ec4f23 100644 --- a/esphome/components/weikai_i2c/weikai_i2c.cpp +++ b/esphome/components/weikai_i2c/weikai_i2c.cpp @@ -96,7 +96,7 @@ void WeikaiRegisterI2C::read_fifo(uint8_t *data, size_t length) const { #endif } else { // error this->comp_->status_set_warning(); - ESP_LOGE(TAG, "WeikaiRegisterI2C::read_fifo() @%02X reg=N/A ch=%d I2C_code:%d len=%d buf=%02X...", address, + ESP_LOGE(TAG, "WeikaiRegisterI2C::read_fifo() @%02X reg=N/A ch=%d I2C_code:%d len=%d buf=%02X", address, this->channel_, (int) error, length, data[0]); } } @@ -131,8 +131,8 @@ void WeikaiRegisterI2C::write_fifo(uint8_t *data, size_t length) { #endif } else { // error this->comp_->status_set_warning(); - ESP_LOGE(TAG, "WK2168Reg::write_fifo() @%02X reg=N/A, ch=%d I2C_code:%d len=%d, buf=%02X...", address, - this->channel_, (int) error, length, data[0]); + ESP_LOGE(TAG, "WK2168Reg::write_fifo() @%02X reg=N/A, ch=%d I2C_code:%d len=%d, buf=%02X", address, this->channel_, + (int) error, length, data[0]); } } @@ -142,7 +142,7 @@ void WeikaiRegisterI2C::write_fifo(uint8_t *data, size_t length) { void WeikaiComponentI2C::setup() { // before any manipulation we store the address to base_address_ for future use this->base_address_ = this->address_; - ESP_LOGCONFIG(TAG, "Setting up wk2168_i2c: %s with %d UARTs at @%02X ...", this->get_name(), this->children_.size(), + ESP_LOGCONFIG(TAG, "Running setup for '%s' with %d UARTs at @%02X", this->get_name(), this->children_.size(), this->base_address_); // enable all channels @@ -160,11 +160,16 @@ void WeikaiComponentI2C::setup() { } void WeikaiComponentI2C::dump_config() { - ESP_LOGCONFIG(TAG, "Initialization of %s with %d UARTs completed", this->get_name(), this->children_.size()); - ESP_LOGCONFIG(TAG, " Crystal: %" PRIu32, this->crystal_); - if (test_mode_) - ESP_LOGCONFIG(TAG, " Test mode: %d", test_mode_); - ESP_LOGCONFIG(TAG, " Transfer buffer size: %d", XFER_MAX_SIZE); + ESP_LOGCONFIG(TAG, + "Initialization of %s with %d UARTs completed\n" + " Crystal: %" PRIu32, + this->get_name(), this->children_.size(), this->crystal_); + if (test_mode_) { + ESP_LOGCONFIG(TAG, + " Test mode: %d\n" + " Transfer buffer size: %d", + test_mode_, XFER_MAX_SIZE); + } this->address_ = this->base_address_; // we restore the base_address before display (less confusing) LOG_I2C_DEVICE(this); diff --git a/esphome/components/weikai_spi/weikai_spi.cpp b/esphome/components/weikai_spi/weikai_spi.cpp index 22c63bbd2d..a43e0e6599 100644 --- a/esphome/components/weikai_spi/weikai_spi.cpp +++ b/esphome/components/weikai_spi/weikai_spi.cpp @@ -156,7 +156,7 @@ void WeikaiRegisterSPI::write_fifo(uint8_t *data, size_t length) { /////////////////////////////////////////////////////////////////////////////// void WeikaiComponentSPI::setup() { using namespace weikai; - ESP_LOGCONFIG(TAG, "Setting up wk2168_spi: %s with %d UARTs...", this->get_name(), this->children_.size()); + ESP_LOGCONFIG(TAG, "Running setup for '%s' with %d UARTs", this->get_name(), this->children_.size()); this->spi_setup(); // enable all channels this->reg(WKREG_GENA, 0) = GENA_C1EN | GENA_C2EN | GENA_C3EN | GENA_C4EN; @@ -173,11 +173,16 @@ void WeikaiComponentSPI::setup() { } void WeikaiComponentSPI::dump_config() { - ESP_LOGCONFIG(TAG, "Initialization of %s with %d UARTs completed", this->get_name(), this->children_.size()); - ESP_LOGCONFIG(TAG, " Crystal: %" PRIu32 "", this->crystal_); - if (test_mode_) - ESP_LOGCONFIG(TAG, " Test mode: %d", test_mode_); - ESP_LOGCONFIG(TAG, " Transfer buffer size: %d", XFER_MAX_SIZE); + ESP_LOGCONFIG(TAG, + "Initialization of %s with %d UARTs completed\n" + " Crystal: %" PRIu32, + this->get_name(), this->children_.size(), this->crystal_); + if (test_mode_) { + ESP_LOGCONFIG(TAG, + " Test mode: %d\n" + " Transfer buffer size: %d", + test_mode_, XFER_MAX_SIZE); + } LOG_PIN(" CS Pin: ", this->cs_); for (auto *child : this->children_) { diff --git a/esphome/components/wiegand/wiegand.cpp b/esphome/components/wiegand/wiegand.cpp index 10c77a8aa2..5a2bb8deee 100644 --- a/esphome/components/wiegand/wiegand.cpp +++ b/esphome/components/wiegand/wiegand.cpp @@ -1,6 +1,6 @@ #include "wiegand.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace wiegand { diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 4a50553305..f2f9d712fc 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -45,7 +45,7 @@ static const char *const TAG = "wifi"; float WiFiComponent::get_setup_priority() const { return setup_priority::WIFI; } void WiFiComponent::setup() { - ESP_LOGCONFIG(TAG, "Setting up WiFi..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->wifi_pre_setup_(); if (this->enable_on_boot_) { this->start(); @@ -58,8 +58,10 @@ void WiFiComponent::setup() { } void WiFiComponent::start() { - ESP_LOGCONFIG(TAG, "Starting WiFi..."); - ESP_LOGCONFIG(TAG, " Local MAC: %s", get_mac_address_pretty().c_str()); + ESP_LOGCONFIG(TAG, + "Starting\n" + " Local MAC: %s", + get_mac_address_pretty().c_str()); this->last_connected_ = millis(); uint32_t hash = this->has_sta() ? fnv1_hash(App.get_compilation_time()) : 88491487UL; @@ -71,7 +73,7 @@ void WiFiComponent::start() { SavedWifiSettings save{}; if (this->pref_.load(&save)) { - ESP_LOGD(TAG, "Loaded saved wifi settings: %s", save.ssid); + ESP_LOGD(TAG, "Loaded saved settings: %s", save.ssid); WiFiAP sta{}; sta.set_ssid(save.ssid); @@ -122,7 +124,7 @@ void WiFiComponent::start() { void WiFiComponent::loop() { this->wifi_loop_(); - const uint32_t now = millis(); + const uint32_t now = App.get_loop_component_start_time(); if (this->has_sta()) { if (this->is_connected() != this->handled_connected_state_) { @@ -160,7 +162,7 @@ void WiFiComponent::loop() { case WIFI_COMPONENT_STATE_STA_CONNECTED: { if (!this->is_connected()) { - ESP_LOGW(TAG, "WiFi Connection lost... Reconnecting..."); + ESP_LOGW(TAG, "Connection lost; reconnecting"); this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING; this->retry_connect(); } else { @@ -201,7 +203,7 @@ void WiFiComponent::loop() { if (!this->has_ap() && this->reboot_timeout_ != 0) { if (now - this->last_connected_ > this->reboot_timeout_) { - ESP_LOGE(TAG, "Can't connect to WiFi, rebooting..."); + ESP_LOGE(TAG, "Can't connect; rebooting"); App.reboot(); } } @@ -260,15 +262,19 @@ void WiFiComponent::setup_ap_config_() { this->ap_.set_ssid(name); } - ESP_LOGCONFIG(TAG, "Setting up AP..."); + ESP_LOGCONFIG(TAG, "Setting up AP"); - ESP_LOGCONFIG(TAG, " AP SSID: '%s'", this->ap_.get_ssid().c_str()); - ESP_LOGCONFIG(TAG, " AP Password: '%s'", this->ap_.get_password().c_str()); + ESP_LOGCONFIG(TAG, + " AP SSID: '%s'\n" + " AP Password: '%s'", + this->ap_.get_ssid().c_str(), this->ap_.get_password().c_str()); if (this->ap_.get_manual_ip().has_value()) { auto manual = *this->ap_.get_manual_ip(); - ESP_LOGCONFIG(TAG, " AP Static IP: '%s'", manual.static_ip.str().c_str()); - ESP_LOGCONFIG(TAG, " AP Gateway: '%s'", manual.gateway.str().c_str()); - ESP_LOGCONFIG(TAG, " AP Subnet: '%s'", manual.subnet.str().c_str()); + ESP_LOGCONFIG(TAG, + " AP Static IP: '%s'\n" + " AP Gateway: '%s'\n" + " AP Subnet: '%s'", + manual.static_ip.str().c_str(), manual.gateway.str().c_str(), manual.subnet.str().c_str()); } this->ap_setup_ = this->wifi_start_ap_(this->ap_); @@ -310,7 +316,7 @@ void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &pa } void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { - ESP_LOGI(TAG, "WiFi Connecting to '%s'...", ap.get_ssid().c_str()); + ESP_LOGI(TAG, "Connecting to '%s'", ap.get_ssid().c_str()); #ifdef ESPHOME_LOG_HAS_VERBOSE ESP_LOGV(TAG, "Connection Params:"); ESP_LOGV(TAG, " SSID: '%s'", ap.get_ssid().c_str()); @@ -427,7 +433,7 @@ void WiFiComponent::print_connect_params_() { ESP_LOGCONFIG(TAG, " Local MAC: %s", get_mac_address_pretty().c_str()); if (this->is_disabled()) { - ESP_LOGCONFIG(TAG, " WiFi is disabled!"); + ESP_LOGCONFIG(TAG, " Disabled"); return; } ESP_LOGCONFIG(TAG, " SSID: " LOG_SECRET("'%s'"), wifi_ssid().c_str()); @@ -436,22 +442,29 @@ void WiFiComponent::print_connect_params_() { ESP_LOGCONFIG(TAG, " IP Address: %s", ip.str().c_str()); } } - ESP_LOGCONFIG(TAG, " BSSID: " LOG_SECRET("%02X:%02X:%02X:%02X:%02X:%02X"), bssid[0], bssid[1], bssid[2], bssid[3], - bssid[4], bssid[5]); - ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); int8_t rssi = wifi_rssi(); - ESP_LOGCONFIG(TAG, " Signal strength: %d dB %s", rssi, LOG_STR_ARG(get_signal_bars(rssi))); + ESP_LOGCONFIG(TAG, + " BSSID: " LOG_SECRET("%02X:%02X:%02X:%02X:%02X:%02X") "\n" + " Hostname: '%s'\n" + " Signal strength: %d dB %s", + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], App.get_name().c_str(), rssi, + LOG_STR_ARG(get_signal_bars(rssi))); if (this->selected_ap_.get_bssid().has_value()) { ESP_LOGV(TAG, " Priority: %.1f", this->get_sta_priority(*this->selected_ap_.get_bssid())); } - ESP_LOGCONFIG(TAG, " Channel: %" PRId32, get_wifi_channel()); - ESP_LOGCONFIG(TAG, " Subnet: %s", wifi_subnet_mask_().str().c_str()); - ESP_LOGCONFIG(TAG, " Gateway: %s", wifi_gateway_ip_().str().c_str()); - ESP_LOGCONFIG(TAG, " DNS1: %s", wifi_dns_ip_(0).str().c_str()); - ESP_LOGCONFIG(TAG, " DNS2: %s", wifi_dns_ip_(1).str().c_str()); + ESP_LOGCONFIG(TAG, + " Channel: %" PRId32 "\n" + " Subnet: %s\n" + " Gateway: %s\n" + " DNS1: %s\n" + " DNS2: %s", + get_wifi_channel(), wifi_subnet_mask_().str().c_str(), wifi_gateway_ip_().str().c_str(), + wifi_dns_ip_(0).str().c_str(), wifi_dns_ip_(1).str().c_str()); #ifdef USE_WIFI_11KV_SUPPORT - ESP_LOGCONFIG(TAG, " BTM: %s", this->btm_ ? "enabled" : "disabled"); - ESP_LOGCONFIG(TAG, " RRM: %s", this->rrm_ ? "enabled" : "disabled"); + ESP_LOGCONFIG(TAG, + " BTM: %s\n" + " RRM: %s", + this->btm_ ? "enabled" : "disabled", this->rrm_ ? "enabled" : "disabled"); #endif } @@ -459,7 +472,7 @@ void WiFiComponent::enable() { if (this->state_ != WIFI_COMPONENT_STATE_DISABLED) return; - ESP_LOGD(TAG, "Enabling WIFI..."); + ESP_LOGD(TAG, "Enabling"); this->error_from_callback_ = false; this->state_ = WIFI_COMPONENT_STATE_OFF; this->start(); @@ -469,7 +482,7 @@ void WiFiComponent::disable() { if (this->state_ == WIFI_COMPONENT_STATE_DISABLED) return; - ESP_LOGD(TAG, "Disabling WIFI..."); + ESP_LOGD(TAG, "Disabling"); this->state_ = WIFI_COMPONENT_STATE_DISABLED; this->wifi_disconnect_(); this->wifi_mode_(false, false); @@ -479,7 +492,7 @@ bool WiFiComponent::is_disabled() { return this->state_ == WIFI_COMPONENT_STATE_ void WiFiComponent::start_scanning() { this->action_started_ = millis(); - ESP_LOGD(TAG, "Starting scan..."); + ESP_LOGD(TAG, "Starting scan"); this->wifi_scan_start_(this->passive_scan_); this->state_ = WIFI_COMPONENT_STATE_STA_SCANNING; } @@ -614,7 +627,7 @@ void WiFiComponent::check_connecting_finished() { // We won't retry hidden networks unless a reconnect fails more than three times again this->retry_hidden_ = false; - ESP_LOGI(TAG, "WiFi Connected!"); + ESP_LOGI(TAG, "Connected"); this->print_connect_params_(); if (this->has_ap()) { @@ -623,7 +636,7 @@ void WiFiComponent::check_connecting_finished() { captive_portal::global_captive_portal->end(); } #endif - ESP_LOGD(TAG, "Disabling AP..."); + ESP_LOGD(TAG, "Disabling AP"); this->wifi_mode_({}, false); } #ifdef USE_IMPROV @@ -644,7 +657,7 @@ void WiFiComponent::check_connecting_finished() { uint32_t now = millis(); if (now - this->action_started_ > 30000) { - ESP_LOGW(TAG, "Timeout while connecting to WiFi."); + ESP_LOGW(TAG, "Connection timeout"); this->retry_connect(); return; } @@ -660,18 +673,18 @@ void WiFiComponent::check_connecting_finished() { } if (status == WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND) { - ESP_LOGW(TAG, "WiFi network can not be found anymore."); + ESP_LOGW(TAG, "Network no longer found"); this->retry_connect(); return; } if (status == WiFiSTAConnectStatus::ERROR_CONNECT_FAILED) { - ESP_LOGW(TAG, "Connecting to WiFi network failed. Are the credentials wrong?"); + ESP_LOGW(TAG, "Connection failed. Check credentials"); this->retry_connect(); return; } - ESP_LOGW(TAG, "WiFi Unknown connection status %d", (int) status); + ESP_LOGW(TAG, "Unknown connection status %d", (int) status); this->retry_connect(); } @@ -687,14 +700,14 @@ void WiFiComponent::retry_connect() { (this->num_retried_ > 3 || this->error_from_callback_)) { if (this->num_retried_ > 5) { // If retry failed for more than 5 times, let's restart STA - ESP_LOGW(TAG, "Restarting WiFi adapter..."); + ESP_LOGW(TAG, "Restarting WiFi adapter"); this->wifi_mode_(false, {}); delay(100); // NOLINT this->num_retried_ = 0; this->retry_hidden_ = false; } else { // Try hidden networks after 3 failed retries - ESP_LOGD(TAG, "Retrying with hidden networks..."); + ESP_LOGD(TAG, "Retrying with hidden networks"); this->retry_hidden_ = true; this->num_retried_++; } diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index abedfab3a6..982007e47f 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -483,14 +483,16 @@ template class WiFiConfigureAction : public Action, publi // Enable WiFi global_wifi_component->enable(); // Set timeout for the connection - this->set_timeout("wifi-connect-timeout", this->connection_timeout_.value(x...), [this]() { - this->connecting_ = false; + this->set_timeout("wifi-connect-timeout", this->connection_timeout_.value(x...), [this, x...]() { // If the timeout is reached, stop connecting and revert to the old AP global_wifi_component->disable(); global_wifi_component->save_wifi_sta(old_sta_.get_ssid(), old_sta_.get_password()); global_wifi_component->enable(); - // Callback to notify the user that the connection failed - this->error_trigger_->trigger(); + // Start a timeout for the fallback if the connection to the old AP fails + this->set_timeout("wifi-fallback-timeout", this->connection_timeout_.value(x...), [this]() { + this->connecting_ = false; + this->error_trigger_->trigger(); + }); }); } @@ -503,6 +505,7 @@ template class WiFiConfigureAction : public Action, publi if (global_wifi_component->is_connected()) { // The WiFi is connected, stop the timeout and reset the connecting flag this->cancel_timeout("wifi-connect-timeout"); + this->cancel_timeout("wifi-fallback-timeout"); this->connecting_ = false; if (global_wifi_component->wifi_ssid() == this->new_sta_.get_ssid()) { // Callback to notify the user that the connection was successful diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index b7a77fcdc9..2dc3acda77 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -91,7 +91,7 @@ bool WiFiComponent::wifi_mode_(optional sta, optional ap) { bool ret = WiFiClass::mode(set_mode); if (!ret) { - ESP_LOGW(TAG, "Setting WiFi mode failed!"); + ESP_LOGW(TAG, "Setting mode failed"); return false; } @@ -590,7 +590,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ // Mitigate CVE-2020-12638 // https://lbsfilm.at/blog/wpa2-authenticationmode-downgrade-in-espressif-microprocessors if (it.old_mode != WIFI_AUTH_OPEN && it.new_mode == WIFI_AUTH_OPEN) { - ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting..."); + ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting"); // we can't call retry_connect() from this context, so disconnect immediately // and notify main thread with error_from_callback_ err_t err = esp_wifi_disconnect(); diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 8e1c2e70d8..3e121098e7 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -32,11 +32,11 @@ extern "C" { #endif } +#include "esphome/core/application.h" +#include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/hal.h" #include "esphome/core/util.h" -#include "esphome/core/application.h" namespace esphome { namespace wifi { @@ -525,7 +525,7 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { // Mitigate CVE-2020-12638 // https://lbsfilm.at/blog/wpa2-authenticationmode-downgrade-in-espressif-microprocessors if (it.old_mode != AUTH_OPEN && it.new_mode == AUTH_OPEN) { - ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting..."); + ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting"); // we can't call retry_connect() from this context, so disconnect immediately // and notify main thread with error_from_callback_ wifi_station_disconnect(); diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index b02f8ef0ce..eb88ed81ad 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -9,10 +9,10 @@ #include "lwip/err.h" #include "lwip/dns.h" +#include "esphome/core/application.h" +#include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/hal.h" -#include "esphome/core/application.h" #include "esphome/core/util.h" namespace esphome { @@ -50,7 +50,7 @@ bool WiFiComponent::wifi_mode_(optional sta, optional ap) { bool ret = WiFi.mode(static_cast(mode)); if (!ret) { - ESP_LOGW(TAG, "Setting WiFi mode failed!"); + ESP_LOGW(TAG, "Setting mode failed"); } return ret; @@ -315,7 +315,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ // Mitigate CVE-2020-12638 // https://lbsfilm.at/blog/wpa2-authenticationmode-downgrade-in-espressif-microprocessors if (it.old_mode != WIFI_AUTH_OPEN && it.new_mode == WIFI_AUTH_OPEN) { - ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting..."); + ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting"); // we can't call retry_connect() from this context, so disconnect immediately // and notify main thread with error_from_callback_ WiFi.disconnect(); diff --git a/esphome/components/wireguard/wireguard.cpp b/esphome/components/wireguard/wireguard.cpp index 431bf52227..1f61e2dda3 100644 --- a/esphome/components/wireguard/wireguard.cpp +++ b/esphome/components/wireguard/wireguard.cpp @@ -21,13 +21,13 @@ static const char *const TAG = "wireguard"; * Cannot use `static const char*` for LOGMSG_PEER_STATUS on esp8266 platform * because log messages in `Wireguard::update()` method fail. */ -#define LOGMSG_PEER_STATUS "WireGuard remote peer is %s (latest handshake %s)" +#define LOGMSG_PEER_STATUS "Remote peer is %s (latest handshake %s)" static const char *const LOGMSG_ONLINE = "online"; static const char *const LOGMSG_OFFLINE = "offline"; void Wireguard::setup() { - ESP_LOGD(TAG, "initializing WireGuard..."); + ESP_LOGCONFIG(TAG, "Running setup"); this->wg_config_.address = this->address_.c_str(); this->wg_config_.private_key = this->private_key_.c_str(); @@ -45,7 +45,7 @@ void Wireguard::setup() { this->wg_initialized_ = esp_wireguard_init(&(this->wg_config_), &(this->wg_ctx_)); if (this->wg_initialized_ == ESP_OK) { - ESP_LOGI(TAG, "WireGuard initialized"); + ESP_LOGI(TAG, "Initialized"); this->wg_peer_offline_time_ = millis(); this->srctime_->add_on_time_sync_callback(std::bind(&Wireguard::start_connection_, this)); this->defer(std::bind(&Wireguard::start_connection_, this)); // defer to avoid blocking setup @@ -56,7 +56,7 @@ void Wireguard::setup() { } #endif } else { - ESP_LOGE(TAG, "cannot initialize WireGuard, error code %d", this->wg_initialized_); + ESP_LOGE(TAG, "Cannot initialize: error code %d", this->wg_initialized_); this->mark_failed(); } } @@ -67,7 +67,7 @@ void Wireguard::loop() { } if ((this->wg_initialized_ == ESP_OK) && (this->wg_connected_ == ESP_OK) && (!network::is_connected())) { - ESP_LOGV(TAG, "local network connection has been lost, stopping WireGuard..."); + ESP_LOGV(TAG, "Local network connection has been lost, stopping"); this->stop_connection_(); } } @@ -109,7 +109,7 @@ void Wireguard::update() { // check reboot timeout every time the peer is down if (this->enabled_ && this->reboot_timeout_ > 0) { if (millis() - this->wg_peer_offline_time_ > this->reboot_timeout_) { - ESP_LOGE(TAG, "WireGuard remote peer is unreachable, rebooting..."); + ESP_LOGE(TAG, "Remote peer is unreachable; rebooting"); App.reboot(); } } @@ -201,14 +201,14 @@ void Wireguard::disable_auto_proceed() { this->proceed_allowed_ = false; } void Wireguard::enable() { this->enabled_ = true; - ESP_LOGI(TAG, "WireGuard enabled"); + ESP_LOGI(TAG, "Enabled"); this->publish_enabled_state(); } void Wireguard::disable() { this->enabled_ = false; this->defer(std::bind(&Wireguard::stop_connection_, this)); // defer to avoid blocking running loop - ESP_LOGI(TAG, "WireGuard disabled"); + ESP_LOGI(TAG, "Disabled"); this->publish_enabled_state(); } @@ -224,44 +224,44 @@ bool Wireguard::is_enabled() { return this->enabled_; } void Wireguard::start_connection_() { if (!this->enabled_) { - ESP_LOGV(TAG, "WireGuard is disabled, cannot start connection"); + ESP_LOGV(TAG, "Disabled, cannot start connection"); return; } if (this->wg_initialized_ != ESP_OK) { - ESP_LOGE(TAG, "cannot start WireGuard, initialization in error with code %d", this->wg_initialized_); + ESP_LOGE(TAG, "Cannot start: error code %d", this->wg_initialized_); return; } if (!network::is_connected()) { - ESP_LOGD(TAG, "WireGuard is waiting for local network connection to be available"); + ESP_LOGD(TAG, "Waiting for local network connection to be available"); return; } if (!this->srctime_->now().is_valid()) { - ESP_LOGD(TAG, "WireGuard is waiting for system time to be synchronized"); + ESP_LOGD(TAG, "Waiting for system time to be synchronized"); return; } if (this->wg_connected_ == ESP_OK) { - ESP_LOGV(TAG, "WireGuard connection already started"); + ESP_LOGV(TAG, "Connection already started"); return; } - ESP_LOGD(TAG, "starting WireGuard connection..."); + ESP_LOGD(TAG, "Starting connection"); this->wg_connected_ = esp_wireguard_connect(&(this->wg_ctx_)); if (this->wg_connected_ == ESP_OK) { - ESP_LOGI(TAG, "WireGuard connection started"); + ESP_LOGI(TAG, "Connection started"); } else if (this->wg_connected_ == ESP_ERR_RETRY) { - ESP_LOGD(TAG, "WireGuard is waiting for endpoint IP address to be available"); + ESP_LOGD(TAG, "Waiting for endpoint IP address to be available"); return; } else { - ESP_LOGW(TAG, "cannot start WireGuard connection, error code %d", this->wg_connected_); + ESP_LOGW(TAG, "Cannot start connection, error code %d", this->wg_connected_); return; } - ESP_LOGD(TAG, "configuring WireGuard allowed IPs list..."); + ESP_LOGD(TAG, "Configuring allowed IPs list"); bool allowed_ips_ok = true; for (std::tuple ip : this->allowed_ips_) { allowed_ips_ok &= @@ -269,9 +269,9 @@ void Wireguard::start_connection_() { } if (allowed_ips_ok) { - ESP_LOGD(TAG, "allowed IPs list configured correctly"); + ESP_LOGD(TAG, "Allowed IPs list configured correctly"); } else { - ESP_LOGE(TAG, "cannot configure WireGuard allowed IPs list, aborting..."); + ESP_LOGE(TAG, "Cannot configure allowed IPs list, aborting"); this->stop_connection_(); this->mark_failed(); } @@ -279,7 +279,7 @@ void Wireguard::start_connection_() { void Wireguard::stop_connection_() { if (this->wg_initialized_ == ESP_OK && this->wg_connected_ == ESP_OK) { - ESP_LOGD(TAG, "stopping WireGuard connection..."); + ESP_LOGD(TAG, "Stopping connection"); esp_wireguard_disconnect(&(this->wg_ctx_)); this->wg_connected_ = ESP_FAIL; } diff --git a/esphome/components/wl_134/text_sensor.py b/esphome/components/wl_134/text_sensor.py index d10627ab64..1a10396bc6 100644 --- a/esphome/components/wl_134/text_sensor.py +++ b/esphome/components/wl_134/text_sensor.py @@ -1,11 +1,10 @@ import esphome.codegen as cg from esphome.components import text_sensor, uart import esphome.config_validation as cv -from esphome.const import ICON_FINGERPRINT +from esphome.const import CONF_RESET, ICON_FINGERPRINT CODEOWNERS = ["@hobbypunk90"] DEPENDENCIES = ["uart"] -CONF_RESET = "reset" wl134_ns = cg.esphome_ns.namespace("wl_134") Wl134Component = wl134_ns.class_( diff --git a/esphome/components/wled/wled_light_effect.cpp b/esphome/components/wled/wled_light_effect.cpp index 84842dff39..25577ccc11 100644 --- a/esphome/components/wled/wled_light_effect.cpp +++ b/esphome/components/wled/wled_light_effect.cpp @@ -1,8 +1,8 @@ #ifdef USE_ARDUINO #include "wled_light_effect.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_ESP32 #include diff --git a/esphome/components/x9c/x9c.cpp b/esphome/components/x9c/x9c.cpp index 4e7a94266e..ccd0c60b50 100644 --- a/esphome/components/x9c/x9c.cpp +++ b/esphome/components/x9c/x9c.cpp @@ -34,7 +34,7 @@ void X9cOutput::trim_value(int change_amount) { } void X9cOutput::setup() { - ESP_LOGCONFIG(TAG, "Setting up X9C Potentiometer with initial value of %f", this->initial_value_); + ESP_LOGCONFIG(TAG, "Running setup"); this->inc_pin_->get_pin(); this->inc_pin_->setup(); @@ -68,8 +68,10 @@ void X9cOutput::dump_config() { LOG_PIN(" Chip Select Pin: ", this->cs_pin_); LOG_PIN(" Increment Pin: ", this->inc_pin_); LOG_PIN(" Up/Down Pin: ", this->ud_pin_); - ESP_LOGCONFIG(TAG, " Initial Value: %f", this->initial_value_); - ESP_LOGCONFIG(TAG, " Step Delay: %d", this->step_delay_); + ESP_LOGCONFIG(TAG, + " Initial Value: %f\n" + " Step Delay: %d", + this->initial_value_, this->step_delay_); LOG_FLOAT_OUTPUT(this); } diff --git a/esphome/components/xgzp68xx/xgzp68xx.cpp b/esphome/components/xgzp68xx/xgzp68xx.cpp index ad6217845d..52933ebdef 100644 --- a/esphome/components/xgzp68xx/xgzp68xx.cpp +++ b/esphome/components/xgzp68xx/xgzp68xx.cpp @@ -1,7 +1,7 @@ #include "xgzp68xx.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/components/i2c/i2c.h" #include @@ -69,22 +69,24 @@ void XGZP68XXComponent::update() { } void XGZP68XXComponent::setup() { - ESP_LOGD(TAG, "Setting up XGZP68xx..."); + ESP_LOGCONFIG(TAG, "Running setup"); uint8_t config; // Display some sample bits to confirm we are talking to the sensor this->read_register(SYSCONFIG_ADDRESS, &config, 1); - ESP_LOGCONFIG(TAG, "Gain value is %d", (config >> 3) & 0b111); - ESP_LOGCONFIG(TAG, "XGZP68xx started!"); + ESP_LOGCONFIG(TAG, + "Gain value is %d\n" + "XGZP68xx started!", + (config >> 3) & 0b111); } void XGZP68XXComponent::dump_config() { - ESP_LOGCONFIG(TAG, "XGZP68xx"); + ESP_LOGCONFIG(TAG, "XGZP68xx:"); LOG_SENSOR(" ", "Temperature: ", this->temperature_sensor_); LOG_SENSOR(" ", "Pressure: ", this->pressure_sensor_); LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, " Connection with XGZP68xx failed!"); + ESP_LOGE(TAG, " Connection failed"); } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp index baf9cb8075..4642768f90 100644 --- a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp +++ b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp @@ -9,8 +9,10 @@ namespace xiaomi_cgd1 { static const char *const TAG = "xiaomi_cgd1"; void XiaomiCGD1::dump_config() { - ESP_LOGCONFIG(TAG, "Xiaomi CGD1"); - ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str()); + ESP_LOGCONFIG(TAG, + "Xiaomi CGD1\n" + " Bindkey: %s", + format_hex_pretty(this->bindkey_, 16).c_str()); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); diff --git a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp index c74794f4f4..0dcbcbd05c 100644 --- a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp +++ b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp @@ -9,8 +9,10 @@ namespace xiaomi_cgdk2 { static const char *const TAG = "xiaomi_cgdk2"; void XiaomiCGDK2::dump_config() { - ESP_LOGCONFIG(TAG, "Xiaomi CGDK2"); - ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str()); + ESP_LOGCONFIG(TAG, + "Xiaomi CGDK2\n" + " Bindkey: %s", + format_hex_pretty(this->bindkey_, 16).c_str()); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); diff --git a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp index c20c7578d0..f9fffa3f20 100644 --- a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp +++ b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp @@ -9,8 +9,10 @@ namespace xiaomi_cgg1 { static const char *const TAG = "xiaomi_cgg1"; void XiaomiCGG1::dump_config() { - ESP_LOGCONFIG(TAG, "Xiaomi CGG1"); - ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str()); + ESP_LOGCONFIG(TAG, + "Xiaomi CGG1\n" + " Bindkey: %s", + format_hex_pretty(this->bindkey_, 16).c_str()); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); diff --git a/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp b/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp index 45d4cceb52..2bc52b8085 100644 --- a/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp +++ b/esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp @@ -1,6 +1,6 @@ #include "xiaomi_hhccjcy10.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_ESP32 diff --git a/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp b/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp index cc122f2264..dff1228f64 100644 --- a/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp +++ b/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp @@ -9,8 +9,10 @@ namespace xiaomi_lywsd02mmc { static const char *const TAG = "xiaomi_lywsd02mmc"; void XiaomiLYWSD02MMC::dump_config() { - ESP_LOGCONFIG(TAG, "Xiaomi LYWSD02MMC"); - ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str()); + ESP_LOGCONFIG(TAG, + "Xiaomi LYWSD02MMC\n" + " Bindkey: %s", + format_hex_pretty(this->bindkey_, 16).c_str()); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); diff --git a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp index d0319c9474..fb0165a21f 100644 --- a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp +++ b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp @@ -9,8 +9,10 @@ namespace xiaomi_lywsd03mmc { static const char *const TAG = "xiaomi_lywsd03mmc"; void XiaomiLYWSD03MMC::dump_config() { - ESP_LOGCONFIG(TAG, "Xiaomi LYWSD03MMC"); - ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str()); + ESP_LOGCONFIG(TAG, + "Xiaomi LYWSD03MMC\n" + " Bindkey: %s", + format_hex_pretty(this->bindkey_, 16).c_str()); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); diff --git a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp index 9ec2b10e12..90b654873b 100644 --- a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp +++ b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp @@ -9,8 +9,10 @@ namespace xiaomi_mhoc401 { static const char *const TAG = "xiaomi_mhoc401"; void XiaomiMHOC401::dump_config() { - ESP_LOGCONFIG(TAG, "Xiaomi MHOC401"); - ESP_LOGCONFIG(TAG, " Bindkey: %s", format_hex_pretty(this->bindkey_, 16).c_str()); + ESP_LOGCONFIG(TAG, + "Xiaomi MHOC401\n" + " Bindkey: %s", + format_hex_pretty(this->bindkey_, 16).c_str()); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); diff --git a/esphome/components/xl9535/xl9535.cpp b/esphome/components/xl9535/xl9535.cpp index 93c65a4cb7..7bcd98070f 100644 --- a/esphome/components/xl9535/xl9535.cpp +++ b/esphome/components/xl9535/xl9535.cpp @@ -7,7 +7,7 @@ namespace xl9535 { static const char *const TAG = "xl9535"; void XL9535Component::setup() { - ESP_LOGCONFIG(TAG, "Setting up XL9535..."); + ESP_LOGCONFIG(TAG, "Running setup"); // Check to see if the device can read from the register uint8_t port = 0; @@ -22,7 +22,7 @@ void XL9535Component::dump_config() { LOG_I2C_DEVICE(this); if (this->is_failed()) { - ESP_LOGE(TAG, "Communication with XL9535 failed!"); + ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); } } diff --git a/esphome/components/xpt2046/touchscreen/xpt2046.cpp b/esphome/components/xpt2046/touchscreen/xpt2046.cpp index aa11ed4b77..fa99e3afa7 100644 --- a/esphome/components/xpt2046/touchscreen/xpt2046.cpp +++ b/esphome/components/xpt2046/touchscreen/xpt2046.cpp @@ -1,6 +1,6 @@ #include "xpt2046.h" -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include @@ -62,16 +62,17 @@ void XPT2046Component::dump_config() { ESP_LOGCONFIG(TAG, "XPT2046:"); LOG_PIN(" IRQ Pin: ", this->irq_pin_); - ESP_LOGCONFIG(TAG, " X min: %d", this->x_raw_min_); - ESP_LOGCONFIG(TAG, " X max: %d", this->x_raw_max_); - ESP_LOGCONFIG(TAG, " Y min: %d", this->y_raw_min_); - ESP_LOGCONFIG(TAG, " Y max: %d", this->y_raw_max_); - - ESP_LOGCONFIG(TAG, " Swap X/Y: %s", YESNO(this->swap_x_y_)); - ESP_LOGCONFIG(TAG, " Invert X: %s", YESNO(this->invert_x_)); - ESP_LOGCONFIG(TAG, " Invert Y: %s", YESNO(this->invert_y_)); - - ESP_LOGCONFIG(TAG, " threshold: %d", this->threshold_); + ESP_LOGCONFIG(TAG, + " X min: %d\n" + " X max: %d\n" + " Y min: %d\n" + " Y max: %d\n" + " Swap X/Y: %s\n" + " Invert X: %s\n" + " Invert Y: %s\n" + " threshold: %d", + this->x_raw_min_, this->x_raw_max_, this->y_raw_min_, this->y_raw_max_, YESNO(this->swap_x_y_), + YESNO(this->invert_x_), YESNO(this->invert_y_), this->threshold_); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/xpt2046/touchscreen/xpt2046.h b/esphome/components/xpt2046/touchscreen/xpt2046.h index a635c08f82..f691ae2c7b 100644 --- a/esphome/components/xpt2046/touchscreen/xpt2046.h +++ b/esphome/components/xpt2046/touchscreen/xpt2046.h @@ -1,9 +1,9 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/core/automation.h" #include "esphome/components/spi/spi.h" #include "esphome/components/touchscreen/touchscreen.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" diff --git a/esphome/config.py b/esphome/config.py index 4b26b33c78..ca3686a0e6 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -7,7 +7,7 @@ import functools import heapq import logging import re -from typing import Any, Union +from typing import Any import voluptuous as vol @@ -63,7 +63,7 @@ def iter_component_configs(config): yield p_name, platform, p_config -ConfigPath = list[Union[str, int]] +ConfigPath = list[str | int] path_context = contextvars.ContextVar("Config path") @@ -389,6 +389,7 @@ class LoadValidationStep(ConfigValidationStep): result.add_str_error(f"Platform not found: '{p_domain}'", path) continue CORE.loaded_integrations.add(p_name) + CORE.loaded_platforms.add(f"{self.domain}/{p_name}") # Process AUTO_LOAD for load in platform.auto_load: diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 2eabcc8568..964f533215 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -982,23 +982,32 @@ def uuid(value): METRIC_SUFFIXES = { - "E": 1e18, - "P": 1e15, - "T": 1e12, - "G": 1e9, - "M": 1e6, - "k": 1e3, - "da": 10, - "d": 1e-1, - "c": 1e-2, - "m": 0.001, - "µ": 1e-6, - "u": 1e-6, - "n": 1e-9, - "p": 1e-12, - "f": 1e-15, - "a": 1e-18, - "": 1, + "Q": 1e30, # Quetta + "R": 1e27, # Ronna + "Y": 1e24, # Yotta + "Z": 1e21, # Zetta + "E": 1e18, # Exa + "P": 1e15, # Peta + "T": 1e12, # Tera + "G": 1e9, # Giga + "M": 1e6, # Mega + "k": 1e3, # Kilo + "h": 1e2, # Hecto + "da": 1e1, # Deca + "": 1e0, # No prefix + "d": 1e-1, # Deci + "c": 1e-2, # Centi + "m": 1e-3, # Milli + "µ": 1e-6, # Micro + "u": 1e-6, # Micro (same as µ) + "n": 1e-9, # Nano + "p": 1e-12, # Pico + "f": 1e-15, # Femto + "a": 1e-18, # Atto + "z": 1e-21, # Zepto + "y": 1e-24, # Yocto + "r": 1e-27, # Ronto + "q": 1e-30, # Quecto } @@ -1658,6 +1667,26 @@ class OnlyWith(Optional): pass +class OnlyWithout(Optional): + """Set the default value only if the given component is NOT loaded.""" + + def __init__(self, key, component, default=None): + super().__init__(key) + self._component = component + self._default = vol.default_factory(default) + + @property + def default(self): + if self._component not in CORE.loaded_integrations: + return self._default + return vol.UNDEFINED + + @default.setter + def default(self, value): + # Ignore default set from vol.Optional + pass + + def _entity_base_validator(config): if CONF_NAME not in config and CONF_ID not in config: raise Invalid("At least one of 'id:' or 'name:' is required!") diff --git a/esphome/const.py b/esphome/const.py index aad89e0d1a..95a5dbe218 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2025.5.2" +__version__ = "2025.6.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( @@ -735,6 +735,7 @@ CONF_REFRESH = "refresh" CONF_RELABEL = "relabel" CONF_REPEAT = "repeat" CONF_REPOSITORY = "repository" +CONF_RESET = "reset" CONF_RESET_DURATION = "reset_duration" CONF_RESET_PIN = "reset_pin" CONF_RESIZE = "resize" @@ -962,6 +963,7 @@ CONF_WAND_ID = "wand_id" CONF_WARM_WHITE = "warm_white" CONF_WARM_WHITE_COLOR_TEMPERATURE = "warm_white_color_temperature" CONF_WATCHDOG_THRESHOLD = "watchdog_threshold" +CONF_WATCHDOG_TIMEOUT = "watchdog_timeout" CONF_WEB_SERVER = "web_server" CONF_WEB_SERVER_ID = "web_server_id" CONF_WEIGHT = "weight" @@ -974,7 +976,9 @@ CONF_WIND_DIRECTION_DEGREES = "wind_direction_degrees" CONF_WIND_SPEED = "wind_speed" CONF_WINDOW_SIZE = "window_size" CONF_WRITE_PIN = "write_pin" +CONF_X = "x" CONF_X_GRID = "x_grid" +CONF_Y = "y" CONF_Y_GRID = "y_grid" CONF_YEAR = "year" CONF_ZERO = "zero" @@ -1130,11 +1134,13 @@ UNIT_WATT_HOURS = "Wh" # device classes DEVICE_CLASS_APPARENT_POWER = "apparent_power" DEVICE_CLASS_AQI = "aqi" +DEVICE_CLASS_AREA = "area" DEVICE_CLASS_ATMOSPHERIC_PRESSURE = "atmospheric_pressure" DEVICE_CLASS_AWNING = "awning" DEVICE_CLASS_BATTERY = "battery" DEVICE_CLASS_BATTERY_CHARGING = "battery_charging" DEVICE_CLASS_BLIND = "blind" +DEVICE_CLASS_BLOOD_GLUCOSE_CONCENTRATION = "blood_glucose_concentration" DEVICE_CLASS_BUTTON = "button" DEVICE_CLASS_CARBON_DIOXIDE = "carbon_dioxide" DEVICE_CLASS_CARBON_MONOXIDE = "carbon_monoxide" @@ -1153,6 +1159,7 @@ DEVICE_CLASS_DOORBELL = "doorbell" DEVICE_CLASS_DURATION = "duration" DEVICE_CLASS_EMPTY = "" DEVICE_CLASS_ENERGY = "energy" +DEVICE_CLASS_ENERGY_DISTANCE = "energy_distance" DEVICE_CLASS_ENERGY_STORAGE = "energy_storage" DEVICE_CLASS_FIRMWARE = "firmware" DEVICE_CLASS_FREQUENCY = "frequency" @@ -1190,6 +1197,7 @@ DEVICE_CLASS_PRECIPITATION_INTENSITY = "precipitation_intensity" DEVICE_CLASS_PRESENCE = "presence" DEVICE_CLASS_PRESSURE = "pressure" DEVICE_CLASS_PROBLEM = "problem" +DEVICE_CLASS_REACTIVE_ENERGY = "reactive_energy" DEVICE_CLASS_REACTIVE_POWER = "reactive_power" DEVICE_CLASS_RESTART = "restart" DEVICE_CLASS_RUNNING = "running" @@ -1217,6 +1225,7 @@ DEVICE_CLASS_VOLUME_STORAGE = "volume_storage" DEVICE_CLASS_WATER = "water" DEVICE_CLASS_WEIGHT = "weight" DEVICE_CLASS_WINDOW = "window" +DEVICE_CLASS_WIND_DIRECTION = "wind_direction" DEVICE_CLASS_WIND_SPEED = "wind_speed" # state classes diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index 3a02c95c82..e95bd7edcc 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -1,8 +1,9 @@ +from collections import defaultdict import logging import math import os import re -from typing import TYPE_CHECKING, Optional, Union +from typing import TYPE_CHECKING from esphome.const import ( CONF_COMMENT, @@ -326,7 +327,7 @@ class ID: else: self.is_manual = is_manual self.is_declaration = is_declaration - self.type: Optional[MockObjClass] = type + self.type: MockObjClass | None = type def resolve(self, registered_ids): from esphome.config_validation import RESERVED_IDS @@ -476,20 +477,20 @@ class EsphomeCore: # True if command is run from vscode api self.vscode = False # The name of the node - self.name: Optional[str] = None + self.name: str | None = None # The friendly name of the node - self.friendly_name: Optional[str] = None + self.friendly_name: str | None = None # The area / zone of the node - self.area: Optional[str] = None + self.area: str | None = None # Additional data components can store temporary data in # The first key to this dict should always be the integration name self.data = {} # The relative path to the configuration YAML - self.config_path: Optional[str] = None + self.config_path: str | None = None # The relative path to where all build files are stored - self.build_path: Optional[str] = None + self.build_path: str | None = None # The validated configuration, this is None until the config has been validated - self.config: Optional[ConfigType] = None + self.config: ConfigType | None = None # The pending tasks in the task queue (mostly for C++ generation) # This is a priority queue (with heapq) # Each item is a tuple of form: (-priority, unique number, task) @@ -509,11 +510,16 @@ class EsphomeCore: # A set of defines to set for the compile process in esphome/core/defines.h self.defines: set[Define] = set() # A map of all platformio options to apply - self.platformio_options: dict[str, Union[str, list[str]]] = {} + self.platformio_options: dict[str, str | list[str]] = {} # A set of strings of names of loaded integrations, used to find namespace ID conflicts self.loaded_integrations = set() + # A set of strings for platform/integration combos + self.loaded_platforms: set[str] = set() # A set of component IDs to track what Component subclasses are declared self.component_ids = set() + # Dict to track platform entity counts for pre-allocation + # Key: platform name (e.g. "sensor", "binary_sensor"), Value: count + self.platform_counts: defaultdict[str, int] = defaultdict(int) # Whether ESPHome was started in verbose mode self.verbose = False # Whether ESPHome was started in quiet mode @@ -543,10 +549,11 @@ class EsphomeCore: self.platformio_options = {} self.loaded_integrations = set() self.component_ids = set() + self.platform_counts = defaultdict(int) PIN_SCHEMA_REGISTRY.reset() @property - def address(self) -> Optional[str]: + def address(self) -> str | None: if self.config is None: raise ValueError("Config has not been loaded yet") @@ -559,7 +566,7 @@ class EsphomeCore: return None @property - def web_port(self) -> Optional[int]: + def web_port(self) -> int | None: if self.config is None: raise ValueError("Config has not been loaded yet") @@ -572,7 +579,7 @@ class EsphomeCore: return None @property - def comment(self) -> Optional[str]: + def comment(self) -> str | None: if self.config is None: raise ValueError("Config has not been loaded yet") @@ -667,16 +674,17 @@ class EsphomeCore: def using_esp_idf(self): return self.target_framework == "esp-idf" - def add_job(self, func, *args, **kwargs): + def add_job(self, func, *args, **kwargs) -> None: self.event_loop.add_job(func, *args, **kwargs) - def flush_tasks(self): + def flush_tasks(self) -> None: try: self.event_loop.flush_tasks() except RuntimeError as e: raise EsphomeError(str(e)) from e - def add(self, expression): + def add(self, expression, prepend=False) -> "Statement": + """Add an expression or statement to the main setup() block.""" from esphome.cpp_generator import Expression, Statement, statement if isinstance(expression, Expression): @@ -686,11 +694,14 @@ class EsphomeCore: f"Add '{expression}' must be expression or statement, not {type(expression)}" ) - self.main_statements.append(expression) + if prepend: + self.main_statements.insert(0, expression) + else: + self.main_statements.append(expression) _LOGGER.debug("Adding: %s", expression) return expression - def add_global(self, expression, prepend=False): + def add_global(self, expression, prepend=False) -> "Statement": from esphome.cpp_generator import Expression, Statement, statement if isinstance(expression, Expression): @@ -773,7 +784,7 @@ class EsphomeCore: _LOGGER.debug("Adding define: %s", define) return define - def add_platformio_option(self, key: str, value: Union[str, list[str]]) -> None: + def add_platformio_option(self, key: str, value: str | list[str]) -> None: new_val = value old_val = self.platformio_options.get(key) if isinstance(old_val, list): @@ -820,6 +831,14 @@ class EsphomeCore: def has_id(self, id): return id in self.variables + def register_platform_component(self, platform_name: str, var) -> None: + """Register a component for a platform and track its count. + + :param platform_name: The name of the platform (e.g., 'sensor', 'binary_sensor') + :param var: The variable (component) being registered (currently unused but kept for future use) + """ + self.platform_counts[platform_name] += 1 + @property def cpp_main_section(self): from esphome.cpp_generator import statement diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index a71b848499..4ed96f7300 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -2,11 +2,30 @@ #include "esphome/core/log.h" #include "esphome/core/version.h" #include "esphome/core/hal.h" +#include #ifdef USE_STATUS_LED #include "esphome/components/status_led/status_led.h" #endif +#ifdef USE_SOCKET_SELECT_SUPPORT +#include + +#ifdef USE_SOCKET_IMPL_LWIP_SOCKETS +// LWIP sockets implementation +#include +#elif defined(USE_SOCKET_IMPL_BSD_SOCKETS) +// BSD sockets implementation +#ifdef USE_ESP32 +// ESP32 "BSD sockets" are actually LWIP under the hood +#include +#else +// True BSD sockets (e.g., host platform) +#include +#endif +#endif +#endif + namespace esphome { static const char *const TAG = "app"; @@ -26,8 +45,8 @@ void Application::register_component_(Component *comp) { this->components_.push_back(comp); } void Application::setup() { - ESP_LOGI(TAG, "Running through setup()..."); - ESP_LOGV(TAG, "Sorting components by setup priority..."); + ESP_LOGI(TAG, "Running through setup()"); + ESP_LOGV(TAG, "Sorting components by setup priority"); std::stable_sort(this->components_.begin(), this->components_.end(), [](const Component *a, const Component *b) { return a->get_actual_setup_priority() > b->get_actual_setup_priority(); }); @@ -47,7 +66,7 @@ void Application::setup() { [](Component *a, Component *b) { return a->get_loop_priority() > b->get_loop_priority(); }); do { - uint32_t new_app_state = STATUS_LED_WARNING; + uint8_t new_app_state = STATUS_LED_WARNING; this->scheduler.call(); this->feed_wdt(); for (uint32_t j = 0; j <= i; j++) { @@ -68,7 +87,7 @@ void Application::setup() { this->calculate_looping_components_(); } void Application::loop() { - uint32_t new_app_state = 0; + uint8_t new_app_state = 0; this->scheduler.call(); @@ -98,7 +117,9 @@ void Application::loop() { // Use the last component's end time instead of calling millis() again auto elapsed = last_op_end_time - this->last_loop_; if (elapsed >= this->loop_interval_ || HighFrequencyLoopRequester::is_high_frequency()) { - yield(); + // Even if we overran the loop interval, we still need to select() + // to know if any sockets have data ready + this->yield_with_select_(0); } else { uint32_t delay_time = this->loop_interval_ - elapsed; uint32_t next_schedule = this->scheduler.next_schedule_in().value_or(delay_time); @@ -106,7 +127,8 @@ void Application::loop() { // otherwise interval=0 schedules result in constant looping with almost no sleep next_schedule = std::max(next_schedule, delay_time / 2); delay_time = std::min(next_schedule, delay_time); - delay(delay_time); + + this->yield_with_select_(delay_time); } this->last_loop_ = last_op_end_time; @@ -139,15 +161,17 @@ void IRAM_ATTR HOT Application::feed_wdt(uint32_t time) { } } void Application::reboot() { - ESP_LOGI(TAG, "Forcing a reboot..."); + ESP_LOGI(TAG, "Forcing a reboot"); for (auto it = this->components_.rbegin(); it != this->components_.rend(); ++it) { (*it)->on_shutdown(); } arch_restart(); } void Application::safe_reboot() { - ESP_LOGI(TAG, "Rebooting safely..."); + ESP_LOGI(TAG, "Rebooting safely"); run_safe_shutdown_hooks(); + teardown_components(TEARDOWN_TIMEOUT_REBOOT_MS); + run_powerdown_hooks(); arch_restart(); } @@ -160,6 +184,56 @@ void Application::run_safe_shutdown_hooks() { } } +void Application::run_powerdown_hooks() { + for (auto it = this->components_.rbegin(); it != this->components_.rend(); ++it) { + (*it)->on_powerdown(); + } +} + +void Application::teardown_components(uint32_t timeout_ms) { + uint32_t start_time = millis(); + + // Copy all components in reverse order using reverse iterators + // Reverse order matches the behavior of run_safe_shutdown_hooks() above and ensures + // components are torn down in the opposite order of their setup_priority (which is + // used to sort components during Application::setup()) + std::vector pending_components(this->components_.rbegin(), this->components_.rend()); + + uint32_t now = start_time; + while (!pending_components.empty() && (now - start_time) < timeout_ms) { + // Feed watchdog during teardown to prevent triggering + this->feed_wdt(now); + + // Use iterator to safely erase elements + for (auto it = pending_components.begin(); it != pending_components.end();) { + if ((*it)->teardown()) { + // Component finished teardown, erase it + it = pending_components.erase(it); + } else { + // Component still needs time + ++it; + } + } + + // Give some time for I/O operations if components are still pending + if (!pending_components.empty()) { + this->yield_with_select_(1); + } + + // Update time for next iteration + now = millis(); + } + + if (!pending_components.empty()) { + // Note: At this point, connections are either disconnected or in a bad state, + // so this warning will only appear via serial rather than being transmitted to clients + for (auto *component : pending_components) { + ESP_LOGW(TAG, "%s did not complete teardown within %" PRIu32 " ms", component->get_component_source(), + timeout_ms); + } + } +} + void Application::calculate_looping_components_() { for (auto *obj : this->components_) { if (obj->has_overridden_loop()) @@ -167,6 +241,121 @@ void Application::calculate_looping_components_() { } } +#ifdef USE_SOCKET_SELECT_SUPPORT +bool Application::register_socket_fd(int fd) { + // WARNING: This function is NOT thread-safe and must only be called from the main loop + // It modifies socket_fds_ and related variables without locking + if (fd < 0) + return false; + + if (fd >= FD_SETSIZE) { + ESP_LOGE(TAG, "Cannot monitor socket fd %d: exceeds FD_SETSIZE (%d)", fd, FD_SETSIZE); + ESP_LOGE(TAG, "Socket will not be monitored for data - may cause performance issues!"); + return false; + } + + this->socket_fds_.push_back(fd); + this->socket_fds_changed_ = true; + + if (fd > this->max_fd_) { + this->max_fd_ = fd; + } + + return true; +} + +void Application::unregister_socket_fd(int fd) { + // WARNING: This function is NOT thread-safe and must only be called from the main loop + // It modifies socket_fds_ and related variables without locking + if (fd < 0) + return; + + auto it = std::find(this->socket_fds_.begin(), this->socket_fds_.end(), fd); + if (it != this->socket_fds_.end()) { + // Swap with last element and pop - O(1) removal since order doesn't matter + if (it != this->socket_fds_.end() - 1) { + std::swap(*it, this->socket_fds_.back()); + } + this->socket_fds_.pop_back(); + this->socket_fds_changed_ = true; + + // Only recalculate max_fd if we removed the current max + if (fd == this->max_fd_) { + if (this->socket_fds_.empty()) { + this->max_fd_ = -1; + } else { + // Find new max using std::max_element + this->max_fd_ = *std::max_element(this->socket_fds_.begin(), this->socket_fds_.end()); + } + } + } +} + +bool Application::is_socket_ready(int fd) const { + // This function is thread-safe for reading the result of select() + // However, it should only be called after select() has been executed in the main loop + // The read_fds_ is only modified by select() in the main loop + if (fd < 0 || fd >= FD_SETSIZE) + return false; + + return FD_ISSET(fd, &this->read_fds_); +} +#endif + +void Application::yield_with_select_(uint32_t delay_ms) { + // Delay while monitoring sockets. When delay_ms is 0, always yield() to ensure other tasks run + // since select() with 0 timeout only polls without yielding. +#ifdef USE_SOCKET_SELECT_SUPPORT + if (!this->socket_fds_.empty()) { + // Update fd_set if socket list has changed + if (this->socket_fds_changed_) { + FD_ZERO(&this->base_read_fds_); + for (int fd : this->socket_fds_) { + if (fd >= 0 && fd < FD_SETSIZE) { + FD_SET(fd, &this->base_read_fds_); + } + } + this->socket_fds_changed_ = false; + } + + // Copy base fd_set before each select + this->read_fds_ = this->base_read_fds_; + + // Convert delay_ms to timeval + struct timeval tv; + tv.tv_sec = delay_ms / 1000; + tv.tv_usec = (delay_ms - tv.tv_sec * 1000) * 1000; + + // Call select with timeout +#if defined(USE_SOCKET_IMPL_LWIP_SOCKETS) || (defined(USE_ESP32) && defined(USE_SOCKET_IMPL_BSD_SOCKETS)) + int ret = lwip_select(this->max_fd_ + 1, &this->read_fds_, nullptr, nullptr, &tv); +#else + int ret = ::select(this->max_fd_ + 1, &this->read_fds_, nullptr, nullptr, &tv); +#endif + + // Process select() result: + // ret < 0: error (except EINTR which is normal) + // ret > 0: socket(s) have data ready - normal and expected + // ret == 0: timeout occurred - normal and expected + if (ret < 0 && errno != EINTR) { + // Actual error - log and fall back to delay + ESP_LOGW(TAG, "select() failed with errno %d", errno); + delay(delay_ms); + } + // When delay_ms is 0, we need to yield since select(0) doesn't yield + if (delay_ms == 0) { + yield(); + } + } else { + // No sockets registered, use regular delay + delay(delay_ms); + } +#else + // No select support, use regular delay + delay(delay_ms); +#endif +} + Application App; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace esphome diff --git a/esphome/core/application.h b/esphome/core/application.h index aa44d9ba1d..f04ea05d8e 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -9,6 +9,10 @@ #include "esphome/core/preferences.h" #include "esphome/core/scheduler.h" +#ifdef USE_SOCKET_SELECT_SUPPORT +#include +#endif + #ifdef USE_BINARY_SENSOR #include "esphome/components/binary_sensor/binary_sensor.h" #endif @@ -75,10 +79,16 @@ namespace esphome { +// Teardown timeout constant (in milliseconds) +// For reboots, it's more important to shut down quickly than disconnect cleanly +// since we're not entering deep sleep. The only consequence of not shutting down +// cleanly is a warning in the log. +static const uint32_t TEARDOWN_TIMEOUT_REBOOT_MS = 1000; // 1 second for quick reboot + class Application { public: - void pre_setup(const std::string &name, const std::string &friendly_name, const std::string &area, - const char *comment, const char *compilation_time, bool name_add_mac_suffix) { + void pre_setup(const std::string &name, const std::string &friendly_name, const char *area, const char *comment, + const char *compilation_time, bool name_add_mac_suffix) { arch_init(); this->name_add_mac_suffix_ = name_add_mac_suffix; if (name_add_mac_suffix) { @@ -188,6 +198,73 @@ class Application { void register_update(update::UpdateEntity *update) { this->updates_.push_back(update); } #endif + /// Reserve space for components to avoid memory fragmentation + void reserve_components(size_t count) { this->components_.reserve(count); } + +#ifdef USE_BINARY_SENSOR + void reserve_binary_sensor(size_t count) { this->binary_sensors_.reserve(count); } +#endif +#ifdef USE_SWITCH + void reserve_switch(size_t count) { this->switches_.reserve(count); } +#endif +#ifdef USE_BUTTON + void reserve_button(size_t count) { this->buttons_.reserve(count); } +#endif +#ifdef USE_SENSOR + void reserve_sensor(size_t count) { this->sensors_.reserve(count); } +#endif +#ifdef USE_TEXT_SENSOR + void reserve_text_sensor(size_t count) { this->text_sensors_.reserve(count); } +#endif +#ifdef USE_FAN + void reserve_fan(size_t count) { this->fans_.reserve(count); } +#endif +#ifdef USE_COVER + void reserve_cover(size_t count) { this->covers_.reserve(count); } +#endif +#ifdef USE_CLIMATE + void reserve_climate(size_t count) { this->climates_.reserve(count); } +#endif +#ifdef USE_LIGHT + void reserve_light(size_t count) { this->lights_.reserve(count); } +#endif +#ifdef USE_NUMBER + void reserve_number(size_t count) { this->numbers_.reserve(count); } +#endif +#ifdef USE_DATETIME_DATE + void reserve_date(size_t count) { this->dates_.reserve(count); } +#endif +#ifdef USE_DATETIME_TIME + void reserve_time(size_t count) { this->times_.reserve(count); } +#endif +#ifdef USE_DATETIME_DATETIME + void reserve_datetime(size_t count) { this->datetimes_.reserve(count); } +#endif +#ifdef USE_SELECT + void reserve_select(size_t count) { this->selects_.reserve(count); } +#endif +#ifdef USE_TEXT + void reserve_text(size_t count) { this->texts_.reserve(count); } +#endif +#ifdef USE_LOCK + void reserve_lock(size_t count) { this->locks_.reserve(count); } +#endif +#ifdef USE_VALVE + void reserve_valve(size_t count) { this->valves_.reserve(count); } +#endif +#ifdef USE_MEDIA_PLAYER + void reserve_media_player(size_t count) { this->media_players_.reserve(count); } +#endif +#ifdef USE_ALARM_CONTROL_PANEL + void reserve_alarm_control_panel(size_t count) { this->alarm_control_panels_.reserve(count); } +#endif +#ifdef USE_EVENT + void reserve_event(size_t count) { this->events_.reserve(count); } +#endif +#ifdef USE_UPDATE + void reserve_update(size_t count) { this->updates_.reserve(count); } +#endif + /// Register the component in this Application instance. template C *register_component(C *c) { static_assert(std::is_base_of::value, "Only Component subclasses can be registered"); @@ -208,7 +285,7 @@ class Application { const std::string &get_friendly_name() const { return this->friendly_name_; } /// Get the area of this Application set by pre_setup(). - const std::string &get_area() const { return this->area_; } + std::string get_area() const { return this->area_ == nullptr ? "" : this->area_; } /// Get the comment of this Application set by pre_setup(). std::string get_comment() const { return this->comment_; } @@ -247,7 +324,15 @@ class Application { void run_safe_shutdown_hooks(); - uint32_t get_app_state() const { return this->app_state_; } + void run_powerdown_hooks(); + + /** Teardown all components with a timeout. + * + * @param timeout_ms Maximum time to wait for teardown in milliseconds + */ + void teardown_components(uint32_t timeout_ms); + + uint8_t get_app_state() const { return this->app_state_; } #ifdef USE_BINARY_SENSOR const std::vector &get_binary_sensors() { return this->binary_sensors_; } @@ -467,6 +552,19 @@ class Application { Scheduler scheduler; + /// Register/unregister a socket file descriptor to be monitored for read events. +#ifdef USE_SOCKET_SELECT_SUPPORT + /// These functions update the fd_set used by select() in the main loop. + /// WARNING: These functions are NOT thread-safe. They must only be called from the main loop. + /// NOTE: File descriptors >= FD_SETSIZE (typically 10 on ESP) will be rejected with an error. + /// @return true if registration was successful, false if fd exceeds limits + bool register_socket_fd(int fd); + void unregister_socket_fd(int fd); + /// Check if there's data available on a socket without blocking + /// This function is thread-safe for reading, but should be called after select() has run + bool is_socket_ready(int fd) const; +#endif + protected: friend Component; @@ -476,6 +574,9 @@ class Application { void feed_wdt_arch_(); + /// Perform a delay while also monitoring socket file descriptors for readiness + void yield_with_select_(uint32_t delay_ms); + std::vector components_{}; std::vector looping_components_{}; @@ -545,16 +646,25 @@ class Application { std::string name_; std::string friendly_name_; - std::string area_; + const char *area_{nullptr}; const char *comment_{nullptr}; const char *compilation_time_{nullptr}; bool name_add_mac_suffix_; uint32_t last_loop_{0}; uint32_t loop_interval_{16}; size_t dump_config_at_{SIZE_MAX}; - uint32_t app_state_{0}; + uint8_t app_state_{0}; Component *current_component_{nullptr}; uint32_t loop_component_start_time_{0}; + +#ifdef USE_SOCKET_SELECT_SUPPORT + // Socket select management + std::vector socket_fds_; // Vector of all monitored socket file descriptors + bool socket_fds_changed_{false}; // Flag to rebuild base_read_fds_ when socket_fds_ changes + int max_fd_{-1}; // Highest file descriptor number for select() + fd_set base_read_fds_{}; // Cached fd_set rebuilt only when socket_fds_ changes + fd_set read_fds_{}; // Working fd_set for select(), copied from base_read_fds_ +#endif }; /// Global storage of Application pointer - only one Application can exist. diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index 1141e4067d..03c44599e2 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -1,6 +1,7 @@ #include "esphome/core/component.h" #include +#include #include #include "esphome/core/application.h" #include "esphome/core/hal.h" @@ -29,18 +30,20 @@ const float LATE = -100.0f; } // namespace setup_priority -const uint32_t COMPONENT_STATE_MASK = 0xFF; -const uint32_t COMPONENT_STATE_CONSTRUCTION = 0x00; -const uint32_t COMPONENT_STATE_SETUP = 0x01; -const uint32_t COMPONENT_STATE_LOOP = 0x02; -const uint32_t COMPONENT_STATE_FAILED = 0x03; -const uint32_t STATUS_LED_MASK = 0xFF00; -const uint32_t STATUS_LED_OK = 0x0000; -const uint32_t STATUS_LED_WARNING = 0x0100; -const uint32_t STATUS_LED_ERROR = 0x0200; +// Component state uses bits 0-1 (4 states) +const uint8_t COMPONENT_STATE_MASK = 0x03; +const uint8_t COMPONENT_STATE_CONSTRUCTION = 0x00; +const uint8_t COMPONENT_STATE_SETUP = 0x01; +const uint8_t COMPONENT_STATE_LOOP = 0x02; +const uint8_t COMPONENT_STATE_FAILED = 0x03; +// Status LED uses bits 2-3 +const uint8_t STATUS_LED_MASK = 0x0C; +const uint8_t STATUS_LED_OK = 0x00; +const uint8_t STATUS_LED_WARNING = 0x04; // Bit 2 +const uint8_t STATUS_LED_ERROR = 0x08; // Bit 3 -const uint32_t WARN_IF_BLOCKING_OVER_MS = 50U; ///< Initial blocking time allowed without warning -const uint32_t WARN_IF_BLOCKING_INCREMENT_MS = 10U; ///< How long the blocking time must be larger to warn again +const uint16_t WARN_IF_BLOCKING_OVER_MS = 50U; ///< Initial blocking time allowed without warning +const uint16_t WARN_IF_BLOCKING_INCREMENT_MS = 10U; ///< How long the blocking time must be larger to warn again uint32_t global_state = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) @@ -86,9 +89,9 @@ void Component::call_dump_config() { } } -uint32_t Component::get_component_state() const { return this->component_state_; } +uint8_t Component::get_component_state() const { return this->component_state_; } void Component::call() { - uint32_t state = this->component_state_ & COMPONENT_STATE_MASK; + uint8_t state = this->component_state_ & COMPONENT_STATE_MASK; switch (state) { case COMPONENT_STATE_CONSTRUCTION: // State Construction: Call setup and set state to setup @@ -120,7 +123,13 @@ const char *Component::get_component_source() const { } bool Component::should_warn_of_blocking(uint32_t blocking_time) { if (blocking_time > this->warn_if_blocking_over_) { - this->warn_if_blocking_over_ = blocking_time + WARN_IF_BLOCKING_INCREMENT_MS; + // Prevent overflow when adding increment - if we're about to overflow, just max out + if (blocking_time + WARN_IF_BLOCKING_INCREMENT_MS < blocking_time || + blocking_time + WARN_IF_BLOCKING_INCREMENT_MS > std::numeric_limits::max()) { + this->warn_if_blocking_over_ = std::numeric_limits::max(); + } else { + this->warn_if_blocking_over_ = static_cast(blocking_time + WARN_IF_BLOCKING_INCREMENT_MS); + } return true; } return false; @@ -131,6 +140,18 @@ void Component::mark_failed() { this->component_state_ |= COMPONENT_STATE_FAILED; this->status_set_error(); } +void Component::reset_to_construction_state() { + if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) { + ESP_LOGI(TAG, "Component %s is being reset to construction state.", this->get_component_source()); + this->component_state_ &= ~COMPONENT_STATE_MASK; + this->component_state_ |= COMPONENT_STATE_CONSTRUCTION; + // Clear error status when resetting + this->status_clear_error(); + } +} +bool Component::is_in_loop_state() const { + return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP; +} void Component::defer(std::function &&f) { // NOLINT App.scheduler.set_timeout(this, "", 0, std::move(f)); } diff --git a/esphome/core/component.h b/esphome/core/component.h index 7b3e12eb59..d05a965034 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -53,19 +53,19 @@ static const uint32_t SCHEDULER_DONT_RUN = 4294967295UL; ESP_LOGCONFIG(TAG, " Update Interval: %.1fs", this->get_update_interval() / 1000.0f); \ } -extern const uint32_t COMPONENT_STATE_MASK; -extern const uint32_t COMPONENT_STATE_CONSTRUCTION; -extern const uint32_t COMPONENT_STATE_SETUP; -extern const uint32_t COMPONENT_STATE_LOOP; -extern const uint32_t COMPONENT_STATE_FAILED; -extern const uint32_t STATUS_LED_MASK; -extern const uint32_t STATUS_LED_OK; -extern const uint32_t STATUS_LED_WARNING; -extern const uint32_t STATUS_LED_ERROR; +extern const uint8_t COMPONENT_STATE_MASK; +extern const uint8_t COMPONENT_STATE_CONSTRUCTION; +extern const uint8_t COMPONENT_STATE_SETUP; +extern const uint8_t COMPONENT_STATE_LOOP; +extern const uint8_t COMPONENT_STATE_FAILED; +extern const uint8_t STATUS_LED_MASK; +extern const uint8_t STATUS_LED_OK; +extern const uint8_t STATUS_LED_WARNING; +extern const uint8_t STATUS_LED_ERROR; enum class RetryResult { DONE, RETRY }; -extern const uint32_t WARN_IF_BLOCKING_OVER_MS; +extern const uint16_t WARN_IF_BLOCKING_OVER_MS; class Component { public: @@ -110,7 +110,32 @@ class Component { virtual void on_shutdown() {} virtual void on_safe_shutdown() {} - uint32_t get_component_state() const; + /** Called during teardown to allow component to gracefully finish operations. + * + * @return true if teardown is complete, false if more time is needed + */ + virtual bool teardown() { return true; } + + /** Called after teardown is complete to power down hardware. + * + * This is called after all components have finished their teardown process, + * making it safe to power down hardware like ethernet PHY. + */ + virtual void on_powerdown() {} + + uint8_t get_component_state() const; + + /** Reset this component back to the construction state to allow setup to run again. + * + * This can be used by components that have recoverable failures to attempt setup again. + */ + void reset_to_construction_state(); + + /** Check if this component has completed setup and is in the loop state. + * + * @return True if in loop state, false otherwise. + */ + bool is_in_loop_state() const; /** Mark this component as failed. Any future timeouts/intervals/setup/loop will no longer be called. * @@ -285,10 +310,15 @@ class Component { /// Cancel a defer callback using the specified name, name must not be empty. bool cancel_defer(const std::string &name); // NOLINT - uint32_t component_state_{0x0000}; ///< State of this component. + /// State of this component - each bit has a purpose: + /// Bits 0-1: Component state (0x00=CONSTRUCTION, 0x01=SETUP, 0x02=LOOP, 0x03=FAILED) + /// Bit 2: STATUS_LED_WARNING + /// Bit 3: STATUS_LED_ERROR + /// Bits 4-7: Unused - reserved for future expansion (50% of the bits are free) + uint8_t component_state_{0x00}; float setup_priority_override_{NAN}; const char *component_source_{nullptr}; - uint32_t warn_if_blocking_over_{WARN_IF_BLOCKING_OVER_MS}; + uint16_t warn_if_blocking_over_{WARN_IF_BLOCKING_OVER_MS}; ///< Warn if blocked for this many ms (max 65.5s) std::string error_message_{}; }; diff --git a/esphome/core/config.py b/esphome/core/config.py index 72e9f6a65c..c407e1c11a 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -329,6 +329,12 @@ async def _add_automations(config): await automation.build_automation(trigger, [], conf) +@coroutine_with_priority(-100.0) +async def _add_platform_reserves() -> None: + for platform_name, count in sorted(CORE.platform_counts.items()): + cg.add(cg.RawStatement(f"App.reserve_{platform_name}({count});"), prepend=True) + + @coroutine_with_priority(100.0) async def to_code(config): cg.add_global(cg.global_ns.namespace("esphome").using) @@ -347,6 +353,12 @@ async def to_code(config): config[CONF_NAME_ADD_MAC_SUFFIX], ) ) + # Reserve space for components to avoid reallocation during registration + cg.add( + cg.RawStatement(f"App.reserve_components({len(CORE.component_ids)});"), + ) + + CORE.add_job(_add_platform_reserves) CORE.add_job(_add_automations, config) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 8bc554d5f4..f7a937c28d 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -95,6 +95,7 @@ // Feature flags which do not work for zephyr #ifndef USE_ZEPHYR +#define USE_AUDIO_DAC #define USE_AUDIO_FLAC_SUPPORT #define USE_AUDIO_MP3_SUPPORT #define USE_API @@ -139,6 +140,7 @@ #define USE_MICROPHONE #define USE_PSRAM #define USE_SOCKET_IMPL_BSD_SOCKETS +#define USE_SOCKET_SELECT_SUPPORT #define USE_SPEAKER #define USE_SPI #define USE_VOICE_ASSISTANT @@ -152,15 +154,19 @@ #endif #ifdef USE_ESP_IDF -#define USE_ESP_IDF_VERSION_CODE VERSION_CODE(5, 1, 6) +#define USE_ESP_IDF_VERSION_CODE VERSION_CODE(5, 3, 2) #define USE_MICRO_WAKE_WORD #define USE_MICRO_WAKE_WORD_VAD +#if defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32H2) +#define USE_OPENTHREAD +#endif #endif #if defined(USE_ESP32_VARIANT_ESP32S2) #define USE_LOGGER_USB_CDC #elif defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C3) || \ - defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32H2) + defined(USE_ESP32_VARIANT_ESP32C5) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32H2) || \ + defined(USE_ESP32_VARIANT_ESP32P4) #define USE_LOGGER_USB_CDC #define USE_LOGGER_USB_SERIAL_JTAG #endif @@ -197,12 +203,14 @@ #ifdef USE_LIBRETINY #define USE_CAPTIVE_PORTAL #define USE_SOCKET_IMPL_LWIP_SOCKETS +#define USE_SOCKET_SELECT_SUPPORT #define USE_WEBSERVER #define USE_WEBSERVER_PORT 80 // NOLINT #endif #ifdef USE_HOST #define USE_SOCKET_IMPL_BSD_SOCKETS +#define USE_SOCKET_SELECT_SUPPORT #endif // Disabled feature flags diff --git a/esphome/core/entity_base.cpp b/esphome/core/entity_base.cpp index 725a8569a3..791b6615a1 100644 --- a/esphome/core/entity_base.cpp +++ b/esphome/core/entity_base.cpp @@ -12,20 +12,12 @@ void EntityBase::set_name(const char *name) { this->name_ = StringRef(name); if (this->name_.empty()) { this->name_ = StringRef(App.get_friendly_name()); - this->has_own_name_ = false; + this->flags_.has_own_name = false; } else { - this->has_own_name_ = true; + this->flags_.has_own_name = true; } } -// Entity Internal -bool EntityBase::is_internal() const { return this->internal_; } -void EntityBase::set_internal(bool internal) { this->internal_ = internal; } - -// Entity Disabled by Default -bool EntityBase::is_disabled_by_default() const { return this->disabled_by_default_; } -void EntityBase::set_disabled_by_default(bool disabled_by_default) { this->disabled_by_default_ = disabled_by_default; } - // Entity Icon std::string EntityBase::get_icon() const { if (this->icon_c_str_ == nullptr) { @@ -35,14 +27,10 @@ std::string EntityBase::get_icon() const { } void EntityBase::set_icon(const char *icon) { this->icon_c_str_ = icon; } -// Entity Category -EntityCategory EntityBase::get_entity_category() const { return this->entity_category_; } -void EntityBase::set_entity_category(EntityCategory entity_category) { this->entity_category_ = entity_category; } - // Entity Object ID std::string EntityBase::get_object_id() const { // Check if `App.get_friendly_name()` is constant or dynamic. - if (!this->has_own_name_ && App.is_name_add_mac_suffix_enabled()) { + if (!this->flags_.has_own_name && App.is_name_add_mac_suffix_enabled()) { // `App.get_friendly_name()` is dynamic. return str_sanitize(str_snake_case(App.get_friendly_name())); } else { @@ -61,7 +49,7 @@ void EntityBase::set_object_id(const char *object_id) { // Calculate Object ID Hash from Entity Name void EntityBase::calc_object_id_() { // Check if `App.get_friendly_name()` is constant or dynamic. - if (!this->has_own_name_ && App.is_name_add_mac_suffix_enabled()) { + if (!this->flags_.has_own_name && App.is_name_add_mac_suffix_enabled()) { // `App.get_friendly_name()` is dynamic. const auto object_id = str_sanitize(str_snake_case(App.get_friendly_name())); // FNV-1 hash diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index 4ca21f9ee5..6b876a9267 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -20,7 +20,7 @@ class EntityBase { void set_name(const char *name); // Get whether this Entity has its own name or it should use the device friendly_name. - bool has_own_name() const { return this->has_own_name_; } + bool has_own_name() const { return this->flags_.has_own_name; } // Get the sanitized name of this Entity as an ID. std::string get_object_id() const; @@ -29,24 +29,32 @@ class EntityBase { // Get the unique Object ID of this Entity uint32_t get_object_id_hash(); - // Get/set whether this Entity should be hidden from outside of ESPHome - bool is_internal() const; - void set_internal(bool internal); + // Get/set whether this Entity should be hidden outside ESPHome + bool is_internal() const { return this->flags_.internal; } + void set_internal(bool internal) { this->flags_.internal = internal; } // Check if this object is declared to be disabled by default. // That means that when the device gets added to Home Assistant (or other clients) it should // not be added to the default view by default, and a user action is necessary to manually add it. - bool is_disabled_by_default() const; - void set_disabled_by_default(bool disabled_by_default); + bool is_disabled_by_default() const { return this->flags_.disabled_by_default; } + void set_disabled_by_default(bool disabled_by_default) { this->flags_.disabled_by_default = disabled_by_default; } // Get/set the entity category. - EntityCategory get_entity_category() const; - void set_entity_category(EntityCategory entity_category); + EntityCategory get_entity_category() const { return static_cast(this->flags_.entity_category); } + void set_entity_category(EntityCategory entity_category) { + this->flags_.entity_category = static_cast(entity_category); + } // Get/set this entity's icon std::string get_icon() const; void set_icon(const char *icon); + // Check if this entity has state + bool has_state() const { return this->flags_.has_state; } + + // Set has_state - for components that need to manually set this + void set_has_state(bool state) { this->flags_.has_state = state; } + protected: /// The hash_base() function has been deprecated. It is kept in this /// class for now, to prevent external components from not compiling. @@ -56,11 +64,17 @@ class EntityBase { StringRef name_; const char *object_id_c_str_{nullptr}; const char *icon_c_str_{nullptr}; - uint32_t object_id_hash_; - bool has_own_name_{false}; - bool internal_{false}; - bool disabled_by_default_{false}; - EntityCategory entity_category_{ENTITY_CATEGORY_NONE}; + uint32_t object_id_hash_{}; + + // Bit-packed flags to save memory (1 byte instead of 5) + struct EntityFlags { + uint8_t has_own_name : 1; + uint8_t internal : 1; + uint8_t disabled_by_default : 1; + uint8_t has_state : 1; + uint8_t entity_category : 2; // Supports up to 4 categories + uint8_t reserved : 2; // Reserved for future use + } flags_{}; }; class EntityBase_DeviceClass { // NOLINT(readability-identifier-naming) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 36bc7f949b..ec79cb8bbb 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -30,7 +30,6 @@ #elif defined(USE_ESP_IDF) #include #include -#include "esp_mac.h" #include "esp_random.h" #include "esp_system.h" #elif defined(USE_RP2040) @@ -45,6 +44,7 @@ #endif #ifdef USE_ESP32 #include "rom/crc.h" +#include "esp_mac.h" #include "esp_efuse.h" #include "esp_efuse_table.h" #endif diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 4212aeca98..477f260bf0 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -192,15 +192,15 @@ bool random_bytes(uint8_t *data, size_t len); constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb) { return (static_cast(msb) << 8) | (static_cast(lsb)); } +/// Encode a 24-bit value given three bytes in most to least significant byte order. +constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3) { + return (static_cast(byte1) << 16) | (static_cast(byte2) << 8) | (static_cast(byte3)); +} /// Encode a 32-bit value given four bytes in most to least significant byte order. constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4) { return (static_cast(byte1) << 24) | (static_cast(byte2) << 16) | (static_cast(byte3) << 8) | (static_cast(byte4)); } -/// Encode a 24-bit value given three bytes in most to least significant byte order. -constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3) { - return ((static_cast(byte1) << 16) | (static_cast(byte2) << 8) | (static_cast(byte3))); -} /// Encode a value from its constituent bytes (from most to least significant) in an array with length sizeof(T). template::value, int> = 0> @@ -438,7 +438,7 @@ template::value, int> = 0> std::stri } /// Return values for parse_on_off(). -enum ParseOnOffState { +enum ParseOnOffState : uint8_t { PARSE_NONE = 0, PARSE_ON, PARSE_OFF, diff --git a/esphome/core/log.h b/esphome/core/log.h index 9ab80afe78..adf72e4bac 100644 --- a/esphome/core/log.h +++ b/esphome/core/log.h @@ -1,5 +1,7 @@ #pragma once +#include "log_const_en.h" + #include #include // for PRIu32 and friends diff --git a/esphome/core/log_const_en.h b/esphome/core/log_const_en.h new file mode 100644 index 0000000000..ccb1f446e4 --- /dev/null +++ b/esphome/core/log_const_en.h @@ -0,0 +1,4 @@ +#pragma once + +#define ESP_LOG_MSG_COMM_FAIL "Communication failed" +#define ESP_LOG_MSG_COMM_FAIL_FOR "Communication failed for '%s'" diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 2dea450ead..eed222c974 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -2,9 +2,9 @@ #include "application.h" #include "esphome/core/defines.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include #include diff --git a/esphome/coroutine.py b/esphome/coroutine.py index 30ebb8147e..8d952246f3 100644 --- a/esphome/coroutine.py +++ b/esphome/coroutine.py @@ -42,14 +42,13 @@ Here everything is combined in `yield` expressions. You await other coroutines u the last `yield` expression defines what is returned. """ -import collections -from collections.abc import Awaitable, Generator, Iterator +from collections.abc import Awaitable, Callable, Generator, Iterator import functools import heapq import inspect import logging import types -from typing import Any, Callable +from typing import Any _LOGGER = logging.getLogger(__name__) @@ -126,7 +125,7 @@ def _flatten_generator(gen: Generator[Any, Any, Any]): ret = to_send if e.value is None else e.value return ret - if isinstance(val, collections.abc.Awaitable): + if isinstance(val, Awaitable): # yielded object that is awaitable (like `yield some_new_style_method()`) # yield from __await__() like actual coroutines would. to_send = yield from val.__await__() diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 93ebb4cb95..bbfa6af815 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -1,9 +1,9 @@ import abc -from collections.abc import Sequence +from collections.abc import Callable, Sequence import inspect import math import re -from typing import Any, Callable, Optional, Union +from typing import Any from esphome.core import ( CORE, @@ -35,19 +35,19 @@ class Expression(abc.ABC): """ -SafeExpType = Union[ - Expression, - bool, - str, - str, - int, - float, - TimePeriod, - type[bool], - type[int], - type[float], - Sequence[Any], -] +SafeExpType = ( + Expression + | bool + | str + | str + | int + | float + | TimePeriod + | type[bool] + | type[int] + | type[float] + | Sequence[Any] +) class RawExpression(Expression): @@ -90,7 +90,7 @@ class VariableDeclarationExpression(Expression): class ExpressionList(Expression): __slots__ = ("args",) - def __init__(self, *args: Optional[SafeExpType]): + def __init__(self, *args: SafeExpType | None): # Remove every None on end args = list(args) while args and args[-1] is None: @@ -139,7 +139,7 @@ class CallExpression(Expression): class StructInitializer(Expression): __slots__ = ("base", "args") - def __init__(self, base: Expression, *args: tuple[str, Optional[SafeExpType]]): + def __init__(self, base: Expression, *args: tuple[str, SafeExpType | None]): self.base = base # TODO: args is always a Tuple, is this check required? if not isinstance(args, OrderedDict): @@ -197,9 +197,7 @@ class ParameterExpression(Expression): class ParameterListExpression(Expression): __slots__ = ("parameters",) - def __init__( - self, *parameters: Union[ParameterExpression, tuple[SafeExpType, str]] - ): + def __init__(self, *parameters: ParameterExpression | tuple[SafeExpType, str]): self.parameters = [] for parameter in parameters: if not isinstance(parameter, ParameterExpression): @@ -362,7 +360,7 @@ def safe_exp(obj: SafeExpType) -> Expression: return IntLiteral(int(obj.total_seconds)) if isinstance(obj, TimePeriodMinutes): return IntLiteral(int(obj.total_minutes)) - if isinstance(obj, (tuple, list)): + if isinstance(obj, tuple | list): return ArrayInitializer(*[safe_exp(o) for o in obj]) if obj is bool: return bool_ @@ -418,7 +416,9 @@ class LineComment(Statement): self.value = value def __str__(self): - parts = re.sub(r"\\\s*\n", r"\n", self.value, re.MULTILINE).split("\n") + parts = re.sub(r"\\\s*\n", r"\n", self.value, flags=re.MULTILINE).split( + "\n" + ) parts = [f"// {x}" for x in parts] return "\n".join(parts) @@ -461,7 +461,7 @@ def static_const_array(id_, rhs) -> "MockObj": return obj -def statement(expression: Union[Expression, Statement]) -> Statement: +def statement(expression: Expression | Statement) -> Statement: """Convert expression into a statement unless is already a statement.""" if isinstance(expression, Statement): return expression @@ -579,21 +579,21 @@ def new_Pvariable(id_: ID, *args: SafeExpType) -> Pvariable: return Pvariable(id_, rhs) -def add(expression: Union[Expression, Statement]): +def add(expression: Expression | Statement, prepend: bool = False): """Add an expression to the codegen section. After this is called, the given given expression will show up in the setup() function after this has been called. """ - CORE.add(expression) + CORE.add(expression, prepend) -def add_global(expression: Union[SafeExpType, Statement], prepend: bool = False): +def add_global(expression: SafeExpType | Statement, prepend: bool = False): """Add an expression to the codegen global storage (above setup()).""" CORE.add_global(expression, prepend) -def add_library(name: str, version: Optional[str], repository: Optional[str] = None): +def add_library(name: str, version: str | None, repository: str | None = None): """Add a library to the codegen library storage. :param name: The name of the library (for example 'AsyncTCP') @@ -619,7 +619,7 @@ def add_define(name: str, value: SafeExpType = None): CORE.add_define(Define(name, safe_exp(value))) -def add_platformio_option(key: str, value: Union[str, list[str]]): +def add_platformio_option(key: str, value: str | list[str]): CORE.add_platformio_option(key, value) @@ -654,7 +654,7 @@ async def process_lambda( parameters: list[tuple[SafeExpType, str]], capture: str = "=", return_type: SafeExpType = None, -) -> Union[LambdaExpression, None]: +) -> LambdaExpression | None: """Process the given lambda value into a LambdaExpression. This is a coroutine because lambdas can depend on other IDs, @@ -711,8 +711,8 @@ def is_template(value): async def templatable( value: Any, args: list[tuple[SafeExpType, str]], - output_type: Optional[SafeExpType], - to_exp: Union[Callable, dict] = None, + output_type: SafeExpType | None, + to_exp: Callable | dict = None, ): """Generate code for a templatable config option. @@ -821,7 +821,7 @@ class MockObj(Expression): assert self.op == "::" return MockObj(f"using namespace {self.base}") - def __getitem__(self, item: Union[str, Expression]) -> "MockObj": + def __getitem__(self, item: str | Expression) -> "MockObj": next_op = "." if isinstance(item, str) and item.startswith("P"): item = item[1:] diff --git a/esphome/dashboard/core.py b/esphome/dashboard/core.py index 416442c426..410ef0c29d 100644 --- a/esphome/dashboard/core.py +++ b/esphome/dashboard/core.py @@ -1,7 +1,7 @@ from __future__ import annotations import asyncio -from collections.abc import Coroutine +from collections.abc import Callable, Coroutine import contextlib from dataclasses import dataclass from functools import partial @@ -9,7 +9,7 @@ import json import logging from pathlib import Path import threading -from typing import Any, Callable +from typing import Any from esphome.storage_json import ignored_devices_storage_path diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index ddef2e7e2b..529a0815b8 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -2,7 +2,7 @@ from __future__ import annotations import asyncio import base64 -from collections.abc import Iterable +from collections.abc import Callable, Iterable import datetime import functools import gzip @@ -17,7 +17,7 @@ import shutil import subprocess import threading import time -from typing import TYPE_CHECKING, Any, Callable, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar from urllib.parse import urlparse import tornado diff --git a/esphome/git.py b/esphome/git.py index 144c160b20..005bcae702 100644 --- a/esphome/git.py +++ b/esphome/git.py @@ -1,3 +1,4 @@ +from collections.abc import Callable from dataclasses import dataclass from datetime import datetime import hashlib @@ -5,7 +6,6 @@ import logging from pathlib import Path import re import subprocess -from typing import Callable, Optional import urllib.parse import esphome.config_validation as cv @@ -45,12 +45,12 @@ def clone_or_update( *, url: str, ref: str = None, - refresh: Optional[TimePeriodSeconds], + refresh: TimePeriodSeconds | None, domain: str, username: str = None, password: str = None, - submodules: Optional[list[str]] = None, -) -> tuple[Path, Optional[Callable[[], None]]]: + submodules: list[str] | None = None, +) -> tuple[Path, Callable[[], None] | None]: key = f"{url}@{ref}" if username is not None and password is not None: diff --git a/esphome/helpers.py b/esphome/helpers.py index b649465d69..d95546ac94 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -7,7 +7,6 @@ from pathlib import Path import platform import re import tempfile -from typing import Union from urllib.parse import urlparse _LOGGER = logging.getLogger(__name__) @@ -219,8 +218,8 @@ def sort_ip_addresses(address_list: list[str]) -> list[str]: int, int, int, - Union[str, None], - Union[tuple[str, int], tuple[str, int, int, int]], + str | None, + tuple[str, int] | tuple[str, int, int, int], ] ] = [] for addr in address_list: @@ -282,7 +281,7 @@ def read_file(path): raise EsphomeError(f"Error reading file {path}: {err}") from err -def _write_file(path: Union[Path, str], text: Union[str, bytes]): +def _write_file(path: Path | str, text: str | bytes): """Atomically writes `text` to the given path. Automatically creates all parent directories. @@ -315,7 +314,7 @@ def _write_file(path: Union[Path, str], text: Union[str, bytes]): _LOGGER.error("Write file cleanup failed: %s", err) -def write_file(path: Union[Path, str], text: str): +def write_file(path: Path | str, text: str): try: _write_file(path, text) except OSError as err: @@ -324,7 +323,7 @@ def write_file(path: Union[Path, str], text: str): raise EsphomeError(f"Could not write file at {path}") from err -def write_file_if_changed(path: Union[Path, str], text: str) -> bool: +def write_file_if_changed(path: Path | str, text: str) -> bool: """Write text to the given path, but not if the contents match already. Returns true if the file was changed. diff --git a/esphome/loader.py b/esphome/loader.py index dbaa2ac661..79a1d7f576 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -1,3 +1,4 @@ +from collections.abc import Callable from contextlib import AbstractContextManager from dataclasses import dataclass import importlib @@ -8,7 +9,7 @@ import logging from pathlib import Path import sys from types import ModuleType -from typing import Any, Callable, Optional +from typing import Any from esphome.const import SOURCE_FILE_EXTENSIONS from esphome.core import CORE @@ -57,7 +58,7 @@ class ComponentManifest: return getattr(self.module, "IS_TARGET_PLATFORM", False) @property - def config_schema(self) -> Optional[Any]: + def config_schema(self) -> Any | None: return getattr(self.module, "CONFIG_SCHEMA", None) @property @@ -69,7 +70,7 @@ class ComponentManifest: return getattr(self.module, "MULTI_CONF_NO_DEFAULT", False) @property - def to_code(self) -> Optional[Callable[[Any], None]]: + def to_code(self) -> Callable[[Any], None] | None: return getattr(self.module, "to_code", None) @property @@ -96,7 +97,7 @@ class ComponentManifest: return getattr(self.module, "INSTANCE_TYPE", None) @property - def final_validate_schema(self) -> Optional[Callable[[ConfigType], None]]: + def final_validate_schema(self) -> Callable[[ConfigType], None] | None: """Components can declare a `FINAL_VALIDATE_SCHEMA` cv.Schema that gets called after the main validation. In that function checks across components can be made. @@ -129,7 +130,7 @@ class ComponentManifest: class ComponentMetaFinder(importlib.abc.MetaPathFinder): def __init__( - self, components_path: Path, allowed_components: Optional[list[str]] = None + self, components_path: Path, allowed_components: list[str] | None = None ) -> None: self._allowed_components = allowed_components self._finders = [] @@ -140,7 +141,7 @@ class ComponentMetaFinder(importlib.abc.MetaPathFinder): continue self._finders.append(finder) - def find_spec(self, fullname: str, path: Optional[list[str]], target=None): + def find_spec(self, fullname: str, path: list[str] | None, target=None): if not fullname.startswith("esphome.components."): return None parts = fullname.split(".") @@ -167,7 +168,7 @@ def clear_component_meta_finders(): def install_meta_finder( - components_path: Path, allowed_components: Optional[list[str]] = None + components_path: Path, allowed_components: list[str] | None = None ): sys.meta_path.insert(0, ComponentMetaFinder(components_path, allowed_components)) diff --git a/esphome/log.py b/esphome/log.py index 7e69a2fef8..0e91eb32c2 100644 --- a/esphome/log.py +++ b/esphome/log.py @@ -59,7 +59,13 @@ class ESPHomeLogFormatter(logging.Formatter): "ERROR": AnsiFore.RED.value, "CRITICAL": AnsiFore.RED.value, }.get(record.levelname, "") - return f"{prefix}{formatted}{AnsiStyle.RESET_ALL.value}" + message = f"{prefix}{formatted}{AnsiStyle.RESET_ALL.value}" + if CORE.dashboard: + try: + message = message.replace("\033", "\\033") + except UnicodeEncodeError: + pass + return message def setup_log( diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index ed95fa125e..808db03231 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -5,7 +5,6 @@ import os from pathlib import Path import re import subprocess -from typing import Union from esphome.const import CONF_COMPILE_PROCESS_LIMIT, CONF_ESPHOME, KEY_CORE from esphome.core import CORE, EsphomeError @@ -73,7 +72,7 @@ FILTER_PLATFORMIO_LINES = [ ] -def run_platformio_cli(*args, **kwargs) -> Union[str, int]: +def run_platformio_cli(*args, **kwargs) -> str | int: os.environ["PLATFORMIO_FORCE_COLOR"] = "true" os.environ["PLATFORMIO_BUILD_DIR"] = os.path.abspath(CORE.relative_pioenvs_path()) os.environ.setdefault( @@ -93,7 +92,7 @@ def run_platformio_cli(*args, **kwargs) -> Union[str, int]: return run_external_command(platformio.__main__.main, *cmd, **kwargs) -def run_platformio_cli_run(config, verbose, *args, **kwargs) -> Union[str, int]: +def run_platformio_cli_run(config, verbose, *args, **kwargs) -> str | int: command = ["run", "-d", CORE.build_path] if verbose: command += ["-v"] diff --git a/esphome/storage_json.py b/esphome/storage_json.py index fa9fe43d4d..b69dc2dd3f 100644 --- a/esphome/storage_json.py +++ b/esphome/storage_json.py @@ -46,15 +46,16 @@ class StorageJSON: storage_version: int, name: str, friendly_name: str, - comment: str, - esphome_version: str, + comment: str | None, + esphome_version: str | None, src_version: int | None, address: str, web_port: int | None, target_platform: str, - build_path: str, - firmware_bin_path: str, + build_path: str | None, + firmware_bin_path: str | None, loaded_integrations: set[str], + loaded_platforms: set[str], no_mdns: bool, framework: str | None = None, core_platform: str | None = None, @@ -86,6 +87,8 @@ class StorageJSON: self.firmware_bin_path = firmware_bin_path # A set of strings of names of loaded integrations self.loaded_integrations = loaded_integrations + # A set of strings for platform/integration combos + self.loaded_platforms = loaded_platforms # Is mDNS disabled self.no_mdns = no_mdns # The framework used to compile the firmware @@ -107,6 +110,7 @@ class StorageJSON: "build_path": self.build_path, "firmware_bin_path": self.firmware_bin_path, "loaded_integrations": sorted(self.loaded_integrations), + "loaded_platforms": sorted(self.loaded_platforms), "no_mdns": self.no_mdns, "framework": self.framework, "core_platform": self.core_platform, @@ -138,6 +142,7 @@ class StorageJSON: build_path=esph.build_path, firmware_bin_path=esph.firmware_bin, loaded_integrations=esph.loaded_integrations, + loaded_platforms=esph.loaded_platforms, no_mdns=( CONF_MDNS in esph.config and CONF_DISABLED in esph.config[CONF_MDNS] @@ -164,6 +169,7 @@ class StorageJSON: build_path=None, firmware_bin_path=None, loaded_integrations=set(), + loaded_platforms=set(), no_mdns=False, framework=None, core_platform=platform.lower(), @@ -187,6 +193,7 @@ class StorageJSON: build_path = storage.get("build_path") firmware_bin_path = storage.get("firmware_bin_path") loaded_integrations = set(storage.get("loaded_integrations", [])) + loaded_platforms = set(storage.get("loaded_platforms", [])) no_mdns = storage.get("no_mdns", False) framework = storage.get("framework") core_platform = storage.get("core_platform") @@ -203,6 +210,7 @@ class StorageJSON: build_path, firmware_bin_path, loaded_integrations, + loaded_platforms, no_mdns, framework, core_platform, @@ -252,7 +260,7 @@ class EsphomeStorageJSON: def last_update_check(self, new: datetime) -> None: self.last_update_check_str = new.strftime("%Y-%m-%dT%H:%M:%S") - def to_json(self) -> dict: + def to_json(self) -> str: return f"{json.dumps(self.as_dict(), indent=2)}\n" def save(self, path: str) -> None: diff --git a/esphome/types.py b/esphome/types.py index 4e69e3cbd7..f68f503993 100644 --- a/esphome/types.py +++ b/esphome/types.py @@ -1,19 +1,18 @@ """This helper module tracks commonly used types in the esphome python codebase.""" -from typing import Union - from esphome.core import ID, EsphomeCore, Lambda -ConfigFragmentType = Union[ - str, - int, - float, - None, - dict[Union[str, int], "ConfigFragmentType"], - list["ConfigFragmentType"], - ID, - Lambda, -] +ConfigFragmentType = ( + str + | int + | float + | None + | dict[str | int, "ConfigFragmentType"] + | list["ConfigFragmentType"] + | ID + | Lambda +) + ConfigType = dict[str, ConfigFragmentType] CoreType = EsphomeCore -ConfigPathType = Union[str, int] +ConfigPathType = str | int diff --git a/esphome/util.py b/esphome/util.py index 32fd90cd25..ba26b8adc1 100644 --- a/esphome/util.py +++ b/esphome/util.py @@ -6,7 +6,6 @@ from pathlib import Path import re import subprocess import sys -from typing import Union from esphome import const @@ -162,7 +161,7 @@ class RedirectText: def run_external_command( func, *cmd, capture_stdout: bool = False, filter_lines: str = None -) -> Union[int, str]: +) -> int | str: """ Run a function from an external package that acts like a main method. diff --git a/esphome/wizard.py b/esphome/wizard.py index ca987304e2..7b4d87be63 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -67,20 +67,6 @@ esp8266: """ ESP32_CONFIG = """ -esp32: - board: {board} - framework: - type: arduino -""" - -ESP32S2_CONFIG = """ -esp32: - board: {board} - framework: - type: esp-idf -""" - -ESP32C3_CONFIG = """ esp32: board: {board} framework: @@ -105,8 +91,6 @@ rtl87xx: HARDWARE_BASE_CONFIGS = { "ESP8266": ESP8266_CONFIG, "ESP32": ESP32_CONFIG, - "ESP32S2": ESP32S2_CONFIG, - "ESP32C3": ESP32C3_CONFIG, "RP2040": RP2040_CONFIG, "BK72XX": BK72XX_CONFIG, "RTL87XX": RTL87XX_CONFIG, diff --git a/esphome/writer.py b/esphome/writer.py index 39423db64c..a47112e1fd 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -3,7 +3,6 @@ import logging import os from pathlib import Path import re -from typing import Union from esphome import loader from esphome.config import iter_component_configs, iter_components @@ -108,7 +107,10 @@ def storage_should_clean(old: StorageJSON, new: StorageJSON) -> bool: return True if old.build_path != new.build_path: return True - if old.loaded_integrations != new.loaded_integrations: + if ( + old.loaded_integrations != new.loaded_integrations + or old.loaded_platforms != new.loaded_platforms + ): if new.core_platform == PLATFORM_ESP32: from esphome.components.esp32 import FRAMEWORK_ESP_IDF @@ -132,7 +134,7 @@ def update_storage_json(): new.save(path) -def format_ini(data: dict[str, Union[str, list[str]]]) -> str: +def format_ini(data: dict[str, str | list[str]]) -> str: content = "" for key, value in sorted(data.items()): if isinstance(value, list): diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index cbe3fef272..78deec8e65 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -1,5 +1,6 @@ from __future__ import annotations +from collections.abc import Callable import fnmatch import functools import inspect @@ -8,7 +9,7 @@ from ipaddress import _BaseAddress import logging import math import os -from typing import Any, Callable +from typing import Any import uuid import yaml @@ -603,6 +604,10 @@ class ESPHomeDumper(yaml.SafeDumper): return self.represent_secret(value.id) return self.represent_stringify(value.id) + # The below override configures this dumper to indent output YAML properly: + def increase_indent(self, flow=False, indentless=False): + return super().increase_indent(flow, False) + ESPHomeDumper.add_multi_representer( dict, lambda dumper, value: dumper.represent_mapping("tag:yaml.org,2002:map", value) diff --git a/esphome/zeroconf.py b/esphome/zeroconf.py index c6a143a42f..fa496b3488 100644 --- a/esphome/zeroconf.py +++ b/esphome/zeroconf.py @@ -1,9 +1,9 @@ from __future__ import annotations import asyncio +from collections.abc import Callable from dataclasses import dataclass import logging -from typing import Callable from zeroconf import ( AddressResolver, diff --git a/platformio.ini b/platformio.ini index 23ed89c262..27da883ab3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -126,7 +126,7 @@ lib_deps = HTTPClient ; http_request,nextion (Arduino built-in) ESPmDNS ; mdns (Arduino built-in) DNSServer ; captive_portal (Arduino built-in) - esphome/ESP32-audioI2S@2.0.7 ; i2s_audio + esphome/ESP32-audioI2S@2.2.0 ; i2s_audio droscy/esp_wireguard@0.4.2 ; wireguard esphome/esp-audio-libs@1.1.4 ; audio @@ -140,9 +140,9 @@ extra_scripts = post:esphome/components/esp32/post_build.py.script ; This are common settings for the ESP32 (all variants) using IDF. [common:esp32-idf] extends = common:idf -platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.06/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.13/platform-espressif32.zip platform_packages = - pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.1.6/esp-idf-v5.1.6.zip + pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.3.2/esp-idf-v5.3.2.zip framework = espidf lib_deps = @@ -323,6 +323,17 @@ build_flags = ${flags:clangtidy.build_flags} -DUSE_ESP32_VARIANT_ESP32C3 +;;;;;;;; ESP32-C6 ;;;;;;;; + +[env:esp32c6-idf] +extends = common:esp32-idf +board = esp32-c6-devkitc-1 +board_build.esp-idf.sdkconfig_path = .temp/sdkconfig-esp32c6-idf +build_flags = + ${common:esp32-idf.build_flags} + ${flags:runtime.build_flags} + -DUSE_ESP32_VARIANT_ESP32C6 + ;;;;;;;; ESP32-S2 ;;;;;;;; [env:esp32s2-arduino] @@ -413,6 +424,18 @@ build_flags = ${flags:clangtidy.build_flags} -DUSE_ESP32_VARIANT_ESP32S3 +;;;;;;;; ESP32-P4 ;;;;;;;; + +[env:esp32p4-idf] +extends = common:esp32-idf +board = esp32-p4-evboard + +board_build.esp-idf.sdkconfig_path = .temp/sdkconfig-esp32p4-idf +build_flags = + ${common:esp32-idf.build_flags} + ${flags:runtime.build_flags} + -DUSE_ESP32_VARIANT_ESP32P4 + ;;;;;;;; RP2040 ;;;;;;;; [env:rp2040-pico-arduino] diff --git a/pyproject.toml b/pyproject.toml index 1971f033c8..3bec607150 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools==80.4.0", "wheel>=0.43,<0.46"] +requires = ["setuptools==80.9.0", "wheel>=0.43,<0.46"] build-backend = "setuptools.build_meta" [project] @@ -20,7 +20,7 @@ classifiers = [ "Programming Language :: Python :: 3", "Topic :: Home Automation", ] -requires-python = ">=3.9.0" +requires-python = ">=3.10.0" dynamic = ["dependencies", "optional-dependencies", "version"] @@ -62,7 +62,7 @@ addopts = [ ] [tool.pylint.MAIN] -py-version = "3.9" +py-version = "3.10" ignore = [ "api_pb2.py", ] @@ -106,7 +106,7 @@ expected-line-ending-format = "LF" [tool.ruff] required-version = ">=0.5.0" -target-version = "py39" +target-version = "py310" exclude = ['generated'] [tool.ruff.lint] @@ -126,6 +126,7 @@ ignore = [ "PLR0915", # Too many statements ({statements} > {max_statements}) "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target + "UP038", # https://github.com/astral-sh/ruff/issues/7871 https://github.com/astral-sh/ruff/pull/16681 ] [tool.ruff.lint.isort] diff --git a/requirements.txt b/requirements.txt index e3b3538d12..682f9dbe60 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,11 @@ async_timeout==5.0.1; python_version <= "3.10" -cryptography==44.0.2 +cryptography==45.0.1 voluptuous==0.15.2 PyYAML==6.0.2 paho-mqtt==1.6.1 colorama==0.4.6 icmplib==3.0.4 -tornado==6.4.2 +tornado==6.5.1 tzlocal==5.3.1 # from time tzdata>=2021.1 # from time pyserial==3.5 @@ -13,13 +13,13 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==4.8.1 click==8.1.7 esphome-dashboard==20250514.0 -aioesphomeapi==30.2.0 +aioesphomeapi==32.2.3 zeroconf==0.147.0 puremagic==1.29 -ruamel.yaml==0.18.10 # dashboard_import +ruamel.yaml==0.18.14 # dashboard_import esphome-glyphsets==0.2.0 pillow==10.4.0 -cairosvg==2.7.1 +cairosvg==2.8.2 freetype-py==2.5.1 # esp-idf requires this, but doesn't bundle it by default diff --git a/requirements_test.txt b/requirements_test.txt index 6dd8d883ba..689cd9e75e 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,13 +1,14 @@ pylint==3.3.7 flake8==7.2.0 # also change in .pre-commit-config.yaml when updating -ruff==0.11.9 # also change in .pre-commit-config.yaml when updating -pyupgrade==3.19.1 # also change in .pre-commit-config.yaml when updating +ruff==0.11.13 # also change in .pre-commit-config.yaml when updating +pyupgrade==3.20.0 # also change in .pre-commit-config.yaml when updating pre-commit # Unit tests -pytest==8.3.5 +pytest==8.4.0 pytest-cov==6.1.1 -pytest-mock==3.14.0 +pytest-mock==3.14.1 pytest-asyncio==0.26.0 +pytest-xdist==3.7.0 asyncmock==0.4.2 hypothesis==6.92.1 diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 63c1efa1ee..24b6bef843 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -258,6 +258,14 @@ class TypeInfo(ABC): force: Whether to force encoding the field even if it has a default value """ + @abstractmethod + def get_estimated_size(self) -> int: + """Get estimated size in bytes for this field with typical values. + + Returns: + Estimated size in bytes including field ID and typical data + """ + TYPE_INFO: dict[int, TypeInfo] = {} @@ -291,6 +299,9 @@ class DoubleType(TypeInfo): o = f"ProtoSize::add_fixed_field<8>(total_size, {field_id_size}, {name} != 0.0, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 8 # field ID + 8 bytes for double + @register_type(2) class FloatType(TypeInfo): @@ -310,6 +321,9 @@ class FloatType(TypeInfo): o = f"ProtoSize::add_fixed_field<4>(total_size, {field_id_size}, {name} != 0.0f, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 4 # field ID + 4 bytes for float + @register_type(3) class Int64Type(TypeInfo): @@ -329,6 +343,9 @@ class Int64Type(TypeInfo): o = f"ProtoSize::add_int64_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint + @register_type(4) class UInt64Type(TypeInfo): @@ -348,6 +365,9 @@ class UInt64Type(TypeInfo): o = f"ProtoSize::add_uint64_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint + @register_type(5) class Int32Type(TypeInfo): @@ -367,6 +387,9 @@ class Int32Type(TypeInfo): o = f"ProtoSize::add_int32_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint + @register_type(6) class Fixed64Type(TypeInfo): @@ -386,6 +409,9 @@ class Fixed64Type(TypeInfo): o = f"ProtoSize::add_fixed_field<8>(total_size, {field_id_size}, {name} != 0, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 8 # field ID + 8 bytes fixed + @register_type(7) class Fixed32Type(TypeInfo): @@ -405,6 +431,9 @@ class Fixed32Type(TypeInfo): o = f"ProtoSize::add_fixed_field<4>(total_size, {field_id_size}, {name} != 0, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 4 # field ID + 4 bytes fixed + @register_type(8) class BoolType(TypeInfo): @@ -423,6 +452,9 @@ class BoolType(TypeInfo): o = f"ProtoSize::add_bool_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 1 # field ID + 1 byte + @register_type(9) class StringType(TypeInfo): @@ -443,6 +475,9 @@ class StringType(TypeInfo): o = f"ProtoSize::add_string_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical string + @register_type(11) class MessageType(TypeInfo): @@ -478,6 +513,11 @@ class MessageType(TypeInfo): o = f"ProtoSize::add_message_object(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return ( + self.calculate_field_id_size() + 16 + ) # field ID + 16 bytes estimated submessage + @register_type(12) class BytesType(TypeInfo): @@ -498,6 +538,9 @@ class BytesType(TypeInfo): o = f"ProtoSize::add_string_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical bytes + @register_type(13) class UInt32Type(TypeInfo): @@ -517,6 +560,9 @@ class UInt32Type(TypeInfo): o = f"ProtoSize::add_uint32_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint + @register_type(14) class EnumType(TypeInfo): @@ -544,6 +590,9 @@ class EnumType(TypeInfo): o = f"ProtoSize::add_enum_field(total_size, {field_id_size}, static_cast({name}), {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 1 # field ID + 1 byte typical enum + @register_type(15) class SFixed32Type(TypeInfo): @@ -563,6 +612,9 @@ class SFixed32Type(TypeInfo): o = f"ProtoSize::add_fixed_field<4>(total_size, {field_id_size}, {name} != 0, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 4 # field ID + 4 bytes fixed + @register_type(16) class SFixed64Type(TypeInfo): @@ -582,6 +634,9 @@ class SFixed64Type(TypeInfo): o = f"ProtoSize::add_fixed_field<8>(total_size, {field_id_size}, {name} != 0, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 8 # field ID + 8 bytes fixed + @register_type(17) class SInt32Type(TypeInfo): @@ -601,6 +656,9 @@ class SInt32Type(TypeInfo): o = f"ProtoSize::add_sint32_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint + @register_type(18) class SInt64Type(TypeInfo): @@ -620,6 +678,9 @@ class SInt64Type(TypeInfo): o = f"ProtoSize::add_sint64_field(total_size, {field_id_size}, {name}, {force_str(force)});" return o + def get_estimated_size(self) -> int: + return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint + class RepeatedTypeInfo(TypeInfo): def __init__(self, field: descriptor.FieldDescriptorProto) -> None: @@ -738,6 +799,15 @@ class RepeatedTypeInfo(TypeInfo): o += "}" return o + def get_estimated_size(self) -> int: + # For repeated fields, estimate underlying type size * 2 (assume 2 items typically) + underlying_size = ( + self._ti.get_estimated_size() + if hasattr(self._ti, "get_estimated_size") + else 8 + ) + return underlying_size * 2 + def build_enum_type(desc) -> tuple[str, str]: """Builds the enum type.""" @@ -762,7 +832,26 @@ def build_enum_type(desc) -> tuple[str, str]: return out, cpp -def build_message_type(desc: descriptor.DescriptorProto) -> tuple[str, str]: +def calculate_message_estimated_size(desc: descriptor.DescriptorProto) -> int: + """Calculate estimated size for a complete message based on typical values.""" + total_size = 0 + + for field in desc.field: + if field.label == 3: # repeated + ti = RepeatedTypeInfo(field) + else: + ti = TYPE_INFO[field.type](field) + + # Add estimated size for this field + total_size += ti.get_estimated_size() + + return total_size + + +def build_message_type( + desc: descriptor.DescriptorProto, + base_class_fields: dict[str, list[descriptor.FieldDescriptorProto]] = None, +) -> tuple[str, str]: public_content: list[str] = [] protected_content: list[str] = [] decode_varint: list[str] = [] @@ -773,13 +862,47 @@ def build_message_type(desc: descriptor.DescriptorProto) -> tuple[str, str]: dump: list[str] = [] size_calc: list[str] = [] + # Check if this message has a base class + base_class = get_base_class(desc) + common_field_names = set() + if base_class and base_class_fields and base_class in base_class_fields: + common_field_names = {f.name for f in base_class_fields[base_class]} + + # Get message ID if it's a service message + message_id: int | None = get_opt(desc, pb.id) + + # Add MESSAGE_TYPE method if this is a service message + if message_id is not None: + # Add static constexpr for message type + public_content.append(f"static constexpr uint16_t MESSAGE_TYPE = {message_id};") + + # Add estimated size constant + estimated_size = calculate_message_estimated_size(desc) + public_content.append( + f"static constexpr uint16_t ESTIMATED_SIZE = {estimated_size};" + ) + + # Add message_name method for debugging + public_content.append("#ifdef HAS_PROTO_MESSAGE_DUMP") + snake_name = camel_to_snake(desc.name) + public_content.append( + f'static constexpr const char *message_name() {{ return "{snake_name}"; }}' + ) + public_content.append("#endif") + for field in desc.field: if field.label == 3: ti = RepeatedTypeInfo(field) else: ti = TYPE_INFO[field.type](field) - protected_content.extend(ti.protected_content) - public_content.extend(ti.public_content) + + # Skip field declarations for fields that are in the base class + # but include their encode/decode logic + if field.name not in common_field_names: + protected_content.extend(ti.protected_content) + public_content.extend(ti.public_content) + + # Always include encode/decode logic for all fields encode.append(ti.encode_content) size_calc.append(ti.get_size_calculation(f"this->{ti.field_name}")) @@ -893,7 +1016,10 @@ def build_message_type(desc: descriptor.DescriptorProto) -> tuple[str, str]: prot += "#endif\n" public_content.append(prot) - out = f"class {desc.name} : public ProtoMessage {{\n" + if base_class: + out = f"class {desc.name} : public {base_class} {{\n" + else: + out = f"class {desc.name} : public ProtoMessage {{\n" out += " public:\n" out += indent("\n".join(public_content)) + "\n" out += "\n" @@ -925,6 +1051,132 @@ def get_opt( return desc.options.Extensions[opt] +def get_base_class(desc: descriptor.DescriptorProto) -> str | None: + """Get the base_class option from a message descriptor.""" + if not desc.options.HasExtension(pb.base_class): + return None + return desc.options.Extensions[pb.base_class] + + +def collect_messages_by_base_class( + messages: list[descriptor.DescriptorProto], +) -> dict[str, list[descriptor.DescriptorProto]]: + """Group messages by their base_class option.""" + base_class_groups = {} + + for msg in messages: + base_class = get_base_class(msg) + if base_class: + if base_class not in base_class_groups: + base_class_groups[base_class] = [] + base_class_groups[base_class].append(msg) + + return base_class_groups + + +def find_common_fields( + messages: list[descriptor.DescriptorProto], +) -> list[descriptor.FieldDescriptorProto]: + """Find fields that are common to all messages in the list.""" + if not messages: + return [] + + # Start with fields from the first message + first_msg_fields = {field.name: field for field in messages[0].field} + common_fields = [] + + # Check each field to see if it exists in all messages with same type + # Field numbers can vary between messages - derived classes handle the mapping + for field_name, field in first_msg_fields.items(): + is_common = True + + for msg in messages[1:]: + found = False + for other_field in msg.field: + if ( + other_field.name == field_name + and other_field.type == field.type + and other_field.label == field.label + ): + found = True + break + + if not found: + is_common = False + break + + if is_common: + common_fields.append(field) + + # Sort by field number to maintain order + common_fields.sort(key=lambda f: f.number) + return common_fields + + +def build_base_class( + base_class_name: str, + common_fields: list[descriptor.FieldDescriptorProto], +) -> tuple[str, str]: + """Build the base class definition and implementation.""" + public_content = [] + protected_content = [] + + # For base classes, we only declare the fields but don't handle encode/decode + # The derived classes will handle encoding/decoding with their specific field numbers + for field in common_fields: + if field.label == 3: # repeated + ti = RepeatedTypeInfo(field) + else: + ti = TYPE_INFO[field.type](field) + + # Only add field declarations, not encode/decode logic + protected_content.extend(ti.protected_content) + public_content.extend(ti.public_content) + + # Build header + out = f"class {base_class_name} : public ProtoMessage {{\n" + out += " public:\n" + + # Add destructor with override + public_content.insert(0, f"~{base_class_name}() override = default;") + + # Base classes don't implement encode/decode/calculate_size + # Derived classes handle these with their specific field numbers + cpp = "" + + out += indent("\n".join(public_content)) + "\n" + out += "\n" + out += " protected:\n" + out += indent("\n".join(protected_content)) + if protected_content: + out += "\n" + out += "};\n" + + # No implementation needed for base classes + + return out, cpp + + +def generate_base_classes( + base_class_groups: dict[str, list[descriptor.DescriptorProto]], +) -> tuple[str, str]: + """Generate all base classes.""" + all_headers = [] + all_cpp = [] + + for base_class_name, messages in base_class_groups.items(): + # Find common fields + common_fields = find_common_fields(messages) + + if common_fields: + # Generate base class + header, cpp = build_base_class(base_class_name, common_fields) + all_headers.append(header) + all_cpp.append(cpp) + + return "\n".join(all_headers), "\n".join(all_cpp) + + def build_service_message_type( mt: descriptor.DescriptorProto, ) -> tuple[str, str] | None: @@ -941,24 +1193,18 @@ def build_service_message_type( hout = "" cout = "" + # Store ifdef for later use if ifdef is not None: ifdefs[str(mt.name)] = ifdef - hout += f"#ifdef {ifdef}\n" - cout += f"#ifdef {ifdef}\n" if source in (SOURCE_BOTH, SOURCE_SERVER): - # Generate send - func = f"send_{snake}" - hout += f"bool {func}(const {mt.name} &msg);\n" - cout += f"bool APIServerConnectionBase::{func}(const {mt.name} &msg) {{\n" - if log: - cout += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" - cout += f' ESP_LOGVV(TAG, "{func}: %s", msg.dump().c_str());\n' - cout += "#endif\n" - # cout += f' this->set_nodelay({str(nodelay).lower()});\n' - cout += f" return this->send_message_<{mt.name}>(msg, {id_});\n" - cout += "}\n" + # Don't generate individual send methods anymore + # The generic send_message method will be used instead + pass if source in (SOURCE_BOTH, SOURCE_CLIENT): + # Only add ifdef when we're actually generating content + if ifdef is not None: + hout += f"#ifdef {ifdef}\n" # Generate receive func = f"on_{snake}" hout += f"virtual void {func}(const {mt.name} &value){{}};\n" @@ -977,9 +1223,9 @@ def build_service_message_type( case += "break;" RECEIVE_CASES[id_] = case - if ifdef is not None: - hout += "#endif\n" - cout += "#endif\n" + # Only close ifdef if we opened it + if ifdef is not None: + hout += "#endif\n" return hout, cout @@ -1032,8 +1278,25 @@ def main() -> None: mt = file.message_type + # Collect messages by base class + base_class_groups = collect_messages_by_base_class(mt) + + # Find common fields for each base class + base_class_fields = {} + for base_class_name, messages in base_class_groups.items(): + common_fields = find_common_fields(messages) + if common_fields: + base_class_fields[base_class_name] = common_fields + + # Generate base classes + if base_class_fields: + base_headers, base_cpp = generate_base_classes(base_class_groups) + content += base_headers + cpp += base_cpp + + # Generate message types with base class information for m in mt: - s, c = build_message_type(m) + s, c = build_message_type(m, base_class_fields) content += s cpp += c @@ -1083,6 +1346,29 @@ def main() -> None: hpp += f"class {class_name} : public ProtoService {{\n" hpp += " public:\n" + # Add logging helper method declaration + hpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" + hpp += " protected:\n" + hpp += " void log_send_message_(const char *name, const std::string &dump);\n" + hpp += " public:\n" + hpp += "#endif\n\n" + + # Add generic send_message method + hpp += " template\n" + hpp += " bool send_message(const T &msg) {\n" + hpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" + hpp += " this->log_send_message_(T::message_name(), msg.dump());\n" + hpp += "#endif\n" + hpp += " return this->send_message_(msg, T::MESSAGE_TYPE);\n" + hpp += " }\n\n" + + # Add logging helper method implementation to cpp + cpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" + cpp += f"void {class_name}::log_send_message_(const char *name, const std::string &dump) {{\n" + cpp += ' ESP_LOGVV(TAG, "send_message %s: %s", name, dump.c_str());\n' + cpp += "}\n" + cpp += "#endif\n\n" + for mt in file.message_type: obj = build_service_message_type(mt) if obj is None: @@ -1155,8 +1441,7 @@ def main() -> None: body += f"this->{func}(msg);\n" else: body += f"{ret} ret = this->{func}(msg);\n" - ret_snake = camel_to_snake(ret) - body += f"if (!this->send_{ret_snake}(ret)) {{\n" + body += "if (!this->send_message(ret)) {\n" body += " this->on_fatal_error();\n" body += "}\n" cpp += indent(body) + "\n" + "}\n" diff --git a/script/ci-custom.py b/script/ci-custom.py index a3a31b2259..fbabbc1e74 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -663,6 +663,7 @@ def lint_trailing_whitespace(fname, match): "esphome/components/valve/valve.h", "esphome/core/component.h", "esphome/core/gpio.h", + "esphome/core/log_const_en.h", "esphome/core/log.h", "tests/custom.h", ], diff --git a/script/devcontainer-post-create b/script/devcontainer-post-create index 2d376786ac..f4835ba6aa 100755 --- a/script/devcontainer-post-create +++ b/script/devcontainer-post-create @@ -3,9 +3,6 @@ set -e # set -x -apt update -apt-get install avahi-utils -y - mkdir -p config script/setup diff --git a/script/generate-esp32-boards.py b/script/generate-esp32-boards.py new file mode 100755 index 0000000000..83d0f0c3e0 --- /dev/null +++ b/script/generate-esp32-boards.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 + +import json +import os +import subprocess +import tempfile + +from esphome.components.esp32 import ESP_IDF_PLATFORM_VERSION as ver + +version_str = f"{ver.major}.{ver.minor:02d}.{ver.patch:02d}" +print(f"ESP32 Platform Version: {version_str}") + + +def get_boards(): + with tempfile.TemporaryDirectory() as tempdir: + subprocess.run( + [ + "git", + "clone", + "--depth", + "1", + "--branch", + f"{ver.major}.{ver.minor:02d}.{ver.patch:02d}", + "https://github.com/pioarduino/platform-espressif32", + tempdir, + ], + check=True, + ) + boards_file = os.path.join(tempdir, "boards") + boards = {} + for fname in os.listdir(boards_file): + if not fname.endswith(".json"): + continue + with open(os.path.join(boards_file, fname), encoding="utf-8") as f: + board_info = json.load(f) + mcu = board_info["build"]["mcu"] + name = board_info["name"] + board = fname[:-5] + variant = mcu.upper() + boards[board] = { + "name": name, + "variant": f"VARIANT_{variant}", + } + return boards + + +TEMPLATE = """ "%s": { + "name": "%s", + "variant": %s, + }, +""" + + +def main(): + boards = get_boards() + # open boards.py, delete existing BOARDS variable and write the new boards dict + boards_file_path = os.path.join( + os.path.dirname(__file__), "..", "esphome", "components", "esp32", "boards.py" + ) + with open(boards_file_path, encoding="UTF-8") as f: + lines = f.readlines() + + with open(boards_file_path, "w", encoding="UTF-8") as f: + for line in lines: + if line.startswith("BOARDS = {"): + f.write("BOARDS = {\n") + for board, info in sorted(boards.items()): + f.write(TEMPLATE % (board, info["name"], info["variant"])) + f.write("}\n") + break + + f.write(line) + + +if __name__ == "__main__": + main() + print("ESP32 boards updated successfully.") diff --git a/script/helpers.py b/script/helpers.py index 3c1b0c0ddd..1a0349e434 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -5,7 +5,6 @@ import re import subprocess import colorama -import helpers_zephyr root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, "..", ".."))) basepath = os.path.join(root_path, "esphome") @@ -149,7 +148,9 @@ def load_idedata(environment): Path(temp_folder).mkdir(exist_ok=True) if "nrf" in environment: - data = helpers_zephyr.load_idedata(environment, temp_folder, platformio_ini) + from helpers_zephyr import load_idedata as zephyr_load_idedata + + data = zephyr_load_idedata(environment, temp_folder, platformio_ini) else: stdout = subprocess.check_output( ["pio", "run", "-t", "idedata", "-e", environment] diff --git a/script/integration_test b/script/integration_test new file mode 100755 index 0000000000..d637cdd298 --- /dev/null +++ b/script/integration_test @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +cd "${script_dir}/.." + +set -x + +pytest -vvs --no-cov --tb=native -n 0 tests/integration/ diff --git a/script/lint-python b/script/lint-python index c9f1789160..2c25e4aee0 100755 --- a/script/lint-python +++ b/script/lint-python @@ -137,7 +137,7 @@ def main(): print() print("Running pyupgrade...") print() - PYUPGRADE_TARGET = "--py39-plus" + PYUPGRADE_TARGET = "--py310-plus" for files in filesets: cmd = ["pyupgrade", PYUPGRADE_TARGET] + files log = get_err(*cmd) diff --git a/script/list-components.py b/script/list-components.py index 0d4777436b..0afcaa0f9d 100755 --- a/script/list-components.py +++ b/script/list-components.py @@ -56,6 +56,8 @@ def create_components_graph(): CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0] components_graph = {} + platforms = [] + components = [] for path in components_dir.iterdir(): if not path.is_dir(): @@ -70,6 +72,13 @@ def create_components_graph(): ) sys.exit(1) + components.append((comp, name, path)) + if comp.is_platform_component: + platforms.append(name) + + platforms = set(platforms) + + for comp, name, path in components: for dependency in comp.dependencies: add_item_to_components_graph( components_graph, dependency.split(".")[0], name @@ -84,6 +93,8 @@ def create_components_graph(): for platform_path in path.iterdir(): platform_name = platform_path.stem + if platform_name == name or platform_name not in platforms: + continue platform = get_platform(platform_name, name) if platform is None: continue diff --git a/script/run-in-env.py b/script/run-in-env.py index b03f5f19d3..d9bd01a62f 100644 --- a/script/run-in-env.py +++ b/script/run-in-env.py @@ -7,11 +7,7 @@ import sys def find_and_activate_virtualenv(): - if ( - ("VIRTUAL_ENV" in os.environ) - or os.environ.get("DEVCONTAINER") - or os.environ.get("ESPHOME_NO_VENV") - ): + if "VIRTUAL_ENV" in os.environ: return try: diff --git a/script/setup b/script/setup index acc2ec58b4..b17d3235a7 100755 --- a/script/setup +++ b/script/setup @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")/.." -if [ ! -n "$DEVCONTAINER" ] && [ ! -n "$VIRTUAL_ENV" ] && [ ! "$ESPHOME_NO_VENV" ]; then +if [ ! -n "$VIRTUAL_ENV" ]; then if [ -x "$(command -v uv)" ]; then uv venv venv else diff --git a/script/setup.bat b/script/setup.bat index ea2591bb71..f89d5aea1a 100644 --- a/script/setup.bat +++ b/script/setup.bat @@ -1,8 +1,6 @@ @echo off -if defined DEVCONTAINER goto :install if defined VIRTUAL_ENV goto :install -if defined ESPHOME_NO_VENV goto :install echo Starting the Virtual Environment python -m venv venv diff --git a/tests/components/alarm_control_panel/common.yaml b/tests/components/alarm_control_panel/common.yaml index 218274bad4..5b8ae5a282 100644 --- a/tests/components/alarm_control_panel/common.yaml +++ b/tests/components/alarm_control_panel/common.yaml @@ -19,6 +19,7 @@ alarm_control_panel: - input: bin1 bypass_armed_home: true bypass_armed_night: true + bypass_auto: true on_state: then: - lambda: !lambda |- @@ -38,6 +39,7 @@ alarm_control_panel: - input: bin1 bypass_armed_home: true bypass_armed_night: true + bypass_auto: true on_disarmed: then: - logger.log: "### DISARMED ###" diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-c3-idf.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..84a9dd4bb4 --- /dev/null +++ b/tests/components/bme68x_bsec2_i2c/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO6 + sda_pin: GPIO7 + +<<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-idf.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-idf.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/bme68x_bsec2_i2c/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-s2-idf.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-s2-idf.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/bme68x_bsec2_i2c/test.esp32-s2-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-s3-idf.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/bme68x_bsec2_i2c/test.esp32-s3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.rp2040-ard.yaml b/tests/components/bme68x_bsec2_i2c/test.rp2040-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/bme68x_bsec2_i2c/test.rp2040-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/cm1106/common.yaml b/tests/components/cm1106/common.yaml new file mode 100644 index 0000000000..a01e78024e --- /dev/null +++ b/tests/components/cm1106/common.yaml @@ -0,0 +1,11 @@ +uart: + - id: uart_cm1106 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: cm1106 + co2: + name: CM1106 CO2 Value + update_interval: 15s diff --git a/tests/components/cm1106/test.esp32-ard.yaml b/tests/components/cm1106/test.esp32-ard.yaml new file mode 100644 index 0000000000..f486544afa --- /dev/null +++ b/tests/components/cm1106/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +<<: !include common.yaml diff --git a/tests/components/cm1106/test.esp32-c3-ard.yaml b/tests/components/cm1106/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..b516342f3b --- /dev/null +++ b/tests/components/cm1106/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/cm1106/test.esp32-c3-idf.yaml b/tests/components/cm1106/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..b516342f3b --- /dev/null +++ b/tests/components/cm1106/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/cm1106/test.esp32-idf.yaml b/tests/components/cm1106/test.esp32-idf.yaml new file mode 100644 index 0000000000..f486544afa --- /dev/null +++ b/tests/components/cm1106/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + +<<: !include common.yaml diff --git a/tests/components/cm1106/test.esp8266-ard.yaml b/tests/components/cm1106/test.esp8266-ard.yaml new file mode 100644 index 0000000000..b516342f3b --- /dev/null +++ b/tests/components/cm1106/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/cm1106/test.rp2040-ard.yaml b/tests/components/cm1106/test.rp2040-ard.yaml new file mode 100644 index 0000000000..b516342f3b --- /dev/null +++ b/tests/components/cm1106/test.rp2040-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/es8388/common.yaml b/tests/components/es8388/common.yaml new file mode 100644 index 0000000000..6a63de5aa1 --- /dev/null +++ b/tests/components/es8388/common.yaml @@ -0,0 +1,25 @@ +esphome: + on_boot: + then: + - audio_dac.mute_off: + - audio_dac.mute_on: + + - audio_dac.set_volume: + volume: 50% + +i2c: + - id: i2c_es8388 + scl: ${scl_pin} + sda: ${sda_pin} + +audio_dac: + - platform: es8388 + id: es8388_parent + +select: + - platform: es8388 + es8388_id: es8388_parent + dac_output: + name: "DAC Output" + adc_input_mic: + name: "ADC Input MIC" diff --git a/tests/components/es8388/test.esp32-ard.yaml b/tests/components/es8388/test.esp32-ard.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/es8388/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/es8388/test.esp32-c3-ard.yaml b/tests/components/es8388/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/es8388/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/es8388/test.esp32-c3-idf.yaml b/tests/components/es8388/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/es8388/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/es8388/test.esp32-idf.yaml b/tests/components/es8388/test.esp32-idf.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/es8388/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/es8388/test.esp8266-ard.yaml b/tests/components/es8388/test.esp8266-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/es8388/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/esp32/test.esp32-idf.yaml b/tests/components/esp32/test.esp32-idf.yaml new file mode 100644 index 0000000000..582b2c22ce --- /dev/null +++ b/tests/components/esp32/test.esp32-idf.yaml @@ -0,0 +1,12 @@ +esp32: + board: esp32dev + framework: + type: esp-idf + advanced: + enable_lwip_mdns_queries: true + enable_lwip_bridge_interface: true + +wifi: + ssid: MySSID + password: password1 + diff --git a/tests/components/esp32_ble/test.esp32-c3-idf.yaml b/tests/components/esp32_ble/test.esp32-c3-idf.yaml index dade44d145..f8defaf28f 100644 --- a/tests/components/esp32_ble/test.esp32-c3-idf.yaml +++ b/tests/components/esp32_ble/test.esp32-c3-idf.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +esp32_ble: + io_capability: keyboard_only + disable_bt_logs: false diff --git a/tests/components/esp32_ble/test.esp32-idf.yaml b/tests/components/esp32_ble/test.esp32-idf.yaml index dade44d145..f8defaf28f 100644 --- a/tests/components/esp32_ble/test.esp32-idf.yaml +++ b/tests/components/esp32_ble/test.esp32-idf.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +esp32_ble: + io_capability: keyboard_only + disable_bt_logs: false diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml index c75ac73ace..d5a9ec9435 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml @@ -2,4 +2,5 @@ substitutions: pin1: GPIO13 pin2: GPIO14 -<<: !include common-ard.yaml +packages: + common: !include common-ard.yaml diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-c3-ard.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-c3-ard.yaml index 7b4560a0d7..2a3cdec60d 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-c3-ard.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-c3-ard.yaml @@ -2,4 +2,5 @@ substitutions: pin1: GPIO3 pin2: GPIO4 -<<: !include common-ard.yaml +packages: + common: !include common-ard.yaml diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-c3-idf.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-c3-idf.yaml index b4110199f1..8feded852c 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-c3-idf.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-c3-idf.yaml @@ -2,4 +2,5 @@ substitutions: pin1: GPIO3 pin2: GPIO4 -<<: !include common-idf.yaml +packages: + common: !include common-idf.yaml diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml index 5adeba4f8c..bb26436e5b 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml @@ -2,4 +2,5 @@ substitutions: pin1: GPIO13 pin2: GPIO14 -<<: !include common-idf.yaml +packages: + common: !include common-idf.yaml diff --git a/tests/components/esp32_rmt_led_strip/test.esp32-s3-idf.yaml b/tests/components/esp32_rmt_led_strip/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..f64bb9d8a5 --- /dev/null +++ b/tests/components/esp32_rmt_led_strip/test.esp32-s3-idf.yaml @@ -0,0 +1,12 @@ +substitutions: + pin1: GPIO3 + pin2: GPIO4 + +packages: + common: !include common-idf.yaml + +light: + - id: !extend led_strip1 + use_dma: "true" + - id: !extend led_strip2 + use_dma: "false" diff --git a/tests/components/esp_ldo/test.esp32-p4-idf.yaml b/tests/components/esp_ldo/test.esp32-p4-idf.yaml new file mode 100644 index 0000000000..0e91aaf082 --- /dev/null +++ b/tests/components/esp_ldo/test.esp32-p4-idf.yaml @@ -0,0 +1,15 @@ +esp_ldo: + - id: ldo_id + channel: 3 + voltage: 2.5V + adjustable: true + - id: ldo_4 + channel: 4 + voltage: 2.0V + +esphome: + on_boot: + then: + - esp_ldo.voltage.adjust: + id: ldo_id + voltage: !lambda return 2.5; diff --git a/tests/components/inkplate6/test.esp32-idf.yaml b/tests/components/inkplate6/test.esp32-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/inkplate6/test.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/lc709203f/common.yaml b/tests/components/lc709203f/common.yaml new file mode 100644 index 0000000000..53177c0d4a --- /dev/null +++ b/tests/components/lc709203f/common.yaml @@ -0,0 +1,16 @@ +i2c: + - id: i2c_lc709203f + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: lc709203f + size: 2000 + voltage: 3.7 + battery_voltage: + name: "Battery Voltage" + battery_level: + name: "Battery" + temperature: + name: "Pack Temperature" + b_constant: 0xA5A5 diff --git a/tests/components/lc709203f/test.esp32-ard.yaml b/tests/components/lc709203f/test.esp32-ard.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/lc709203f/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/lc709203f/test.esp32-c3-ard.yaml b/tests/components/lc709203f/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/lc709203f/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/lc709203f/test.esp32-c3-idf.yaml b/tests/components/lc709203f/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/lc709203f/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/lc709203f/test.esp32-idf.yaml b/tests/components/lc709203f/test.esp32-idf.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/lc709203f/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/lc709203f/test.esp8266-ard.yaml b/tests/components/lc709203f/test.esp8266-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/lc709203f/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/lc709203f/test.rp2040-ard.yaml b/tests/components/lc709203f/test.rp2040-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/lc709203f/test.rp2040-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index db55da9225..d8452bdd2a 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -170,6 +170,12 @@ lvgl: lvgl.page.is_showing: page1 then: logger.log: "Yes, page1 showing" + - if: + condition: + lvgl.is_idle: + timeout: !lambda return 5000; + then: + logger.log: LVGL is idle on_unload: - logger.log: page unloaded - lvgl.widget.focus: mark @@ -781,6 +787,13 @@ lvgl: value: !lambda return (int)((float)rand() / RAND_MAX * 100); - tabview: id: tabview_id + tab_style: + border_color: 0x00FF00 + border_width: 6 + items: + text_color: 0x0000FF + content_style: + scrollable: false width: 100% height: 80% position: top diff --git a/tests/components/max7219digit/common.yaml b/tests/components/max7219digit/common.yaml index a5a3bd57fb..84edc7eb3d 100644 --- a/tests/components/max7219digit/common.yaml +++ b/tests/components/max7219digit/common.yaml @@ -14,3 +14,15 @@ display: id: my_matrix lambda: |- it.printdigit("hello"); + +esphome: + on_boot: + - priority: 100 + then: + - max7129digit.invert_off: + - max7129digit.invert_on: + - max7129digit.turn_on: + - max7129digit.turn_off: + - max7129digit.reverse_on: + - max7129digit.reverse_off: + - max7129digit.intensity: 10 diff --git a/tests/components/nextion/common.yaml b/tests/components/nextion/common.yaml index 44d6cdfbc9..767c868d0b 100644 --- a/tests/components/nextion/common.yaml +++ b/tests/components/nextion/common.yaml @@ -281,6 +281,8 @@ display: id: main_lcd update_interval: 5s command_spacing: 5ms + max_commands_per_loop: 20 + max_queue_size: 50 on_sleep: then: lambda: 'ESP_LOGD("display","Display went to sleep");' diff --git a/tests/components/online_image/common.yaml b/tests/components/online_image/common.yaml index 69daa915c5..422a24b540 100644 --- a/tests/components/online_image/common.yaml +++ b/tests/components/online_image/common.yaml @@ -11,6 +11,16 @@ online_image: format: PNG type: BINARY resize: 50x50 + request_headers: + X-Test1: 'Test1' + X-Test2: !lambda 'static int x; return to_string(x++);' + on_download_finished: + lambda: |- + if (cached) { + ESP_LOGD("online_image", "Cache hit: using cached image"); + } else { + ESP_LOGD("online_image", "Cache miss: fresh download"); + } - id: online_binary_transparent_image url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png type: BINARY diff --git a/tests/components/openthread/test.esp32-c6-idf.yaml b/tests/components/openthread/test.esp32-c6-idf.yaml new file mode 100644 index 0000000000..482fd1a453 --- /dev/null +++ b/tests/components/openthread/test.esp32-c6-idf.yaml @@ -0,0 +1,11 @@ +network: + enable_ipv6: true + +openthread: + channel: 13 + network_name: OpenThread-8f28 + network_key: 0xdfd34f0f05cad978ec4e32b0413038ff + pan_id: 0x8f28 + ext_pan_id: 0xd63e8e3e495ebbc3 + pskc: 0xc23a76e98f1a6483639b1ac1271e2e27 + force_dataset: true diff --git a/tests/components/openthread_info/test.esp32-c6-idf.yaml b/tests/components/openthread_info/test.esp32-c6-idf.yaml new file mode 100644 index 0000000000..ded0f17611 --- /dev/null +++ b/tests/components/openthread_info/test.esp32-c6-idf.yaml @@ -0,0 +1,30 @@ +network: + enable_ipv6: true + +openthread: + channel: 13 + network_key: 0xdfd34f0f05cad978ec4e32b0413038ff + pan_id: 0x8f28 + +text_sensor: + - platform: openthread_info + ip_address: + name: "Off-mesh routable IP Address" + channel: + name: "Channel" + role: + name: "Device Role" + rloc16: + name: "RLOC16" + ext_addr: + name: "Extended Address" + eui64: + name: "EUI64" + network_name: + name: "Network Name" + network_key: + name: "Network Key" + pan_id: + name: "PAN ID" + ext_pan_id: + name: "Extended PAN ID" diff --git a/tests/components/psram/test.esp32-p4-idf.yaml b/tests/components/psram/test.esp32-p4-idf.yaml new file mode 100644 index 0000000000..9ebd80328d --- /dev/null +++ b/tests/components/psram/test.esp32-p4-idf.yaml @@ -0,0 +1,9 @@ +esp32: + cpu_frequency: 360MHz + framework: + type: esp-idf + advanced: + enable_idf_experimental_features: yes + +psram: + speed: 200MHz diff --git a/tests/components/psram/test.esp32-s3-idf.yaml b/tests/components/psram/test.esp32-s3-idf.yaml index e0e7fb52f6..75d4ee539c 100644 --- a/tests/components/psram/test.esp32-s3-idf.yaml +++ b/tests/components/psram/test.esp32-s3-idf.yaml @@ -1,4 +1,5 @@ esp32: + cpu_frequency: 240MHz framework: type: esp-idf advanced: diff --git a/tests/components/remote_receiver/esp32-common-idf.yaml b/tests/components/remote_receiver/esp32-common-idf.yaml index b314880f8a..14effcbd2c 100644 --- a/tests/components/remote_receiver/esp32-common-idf.yaml +++ b/tests/components/remote_receiver/esp32-common-idf.yaml @@ -7,7 +7,6 @@ remote_receiver: filter_symbols: ${filter_symbols} receive_symbols: ${receive_symbols} rmt_symbols: ${rmt_symbols} - use_dma: ${use_dma} <<: !include common-actions.yaml binary_sensor: diff --git a/tests/components/remote_receiver/test.esp32-c3-idf.yaml b/tests/components/remote_receiver/test.esp32-c3-idf.yaml index 495bb293c3..f017a2d807 100644 --- a/tests/components/remote_receiver/test.esp32-c3-idf.yaml +++ b/tests/components/remote_receiver/test.esp32-c3-idf.yaml @@ -4,7 +4,6 @@ substitutions: filter_symbols: "2" receive_symbols: "4" rmt_symbols: "64" - use_dma: "true" packages: common: !include esp32-common-idf.yaml diff --git a/tests/components/remote_receiver/test.esp32-idf.yaml b/tests/components/remote_receiver/test.esp32-idf.yaml index 495bb293c3..f017a2d807 100644 --- a/tests/components/remote_receiver/test.esp32-idf.yaml +++ b/tests/components/remote_receiver/test.esp32-idf.yaml @@ -4,7 +4,6 @@ substitutions: filter_symbols: "2" receive_symbols: "4" rmt_symbols: "64" - use_dma: "true" packages: common: !include esp32-common-idf.yaml diff --git a/tests/components/remote_receiver/test.esp32-s3-idf.yaml b/tests/components/remote_receiver/test.esp32-s3-idf.yaml index e678ba456d..74f49866cd 100644 --- a/tests/components/remote_receiver/test.esp32-s3-idf.yaml +++ b/tests/components/remote_receiver/test.esp32-s3-idf.yaml @@ -4,7 +4,10 @@ substitutions: filter_symbols: "2" receive_symbols: "4" rmt_symbols: "64" - use_dma: "true" packages: common: !include esp32-common-idf.yaml + +remote_receiver: + - id: !extend rcvr + use_dma: "true" diff --git a/tests/components/remote_transmitter/esp32-common-idf.yaml b/tests/components/remote_transmitter/esp32-common-idf.yaml index c5d4ab7b96..8b26c45149 100644 --- a/tests/components/remote_transmitter/esp32-common-idf.yaml +++ b/tests/components/remote_transmitter/esp32-common-idf.yaml @@ -4,7 +4,6 @@ remote_transmitter: carrier_duty_percent: 50% clock_resolution: ${clock_resolution} rmt_symbols: ${rmt_symbols} - use_dma: ${use_dma} packages: buttons: !include common-buttons.yaml diff --git a/tests/components/remote_transmitter/test.esp32-c3-idf.yaml b/tests/components/remote_transmitter/test.esp32-c3-idf.yaml index be526014bd..cc1fe69b4d 100644 --- a/tests/components/remote_transmitter/test.esp32-c3-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-c3-idf.yaml @@ -2,7 +2,6 @@ substitutions: pin: GPIO2 clock_resolution: "2000000" rmt_symbols: "64" - use_dma: "true" packages: common: !include esp32-common-idf.yaml diff --git a/tests/components/remote_transmitter/test.esp32-idf.yaml b/tests/components/remote_transmitter/test.esp32-idf.yaml index be526014bd..cc1fe69b4d 100644 --- a/tests/components/remote_transmitter/test.esp32-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-idf.yaml @@ -2,7 +2,6 @@ substitutions: pin: GPIO2 clock_resolution: "2000000" rmt_symbols: "64" - use_dma: "true" packages: common: !include esp32-common-idf.yaml diff --git a/tests/components/remote_transmitter/test.esp32-s3-idf.yaml b/tests/components/remote_transmitter/test.esp32-s3-idf.yaml index cb86020064..d23463b531 100644 --- a/tests/components/remote_transmitter/test.esp32-s3-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-s3-idf.yaml @@ -2,7 +2,10 @@ substitutions: pin: GPIO38 clock_resolution: "2000000" rmt_symbols: "64" - use_dma: "true" packages: common: !include esp32-common-idf.yaml + +remote_transmitter: + - id: !extend xmitr + use_dma: "true" diff --git a/tests/components/spi/test.esp32-p4-idf.yaml b/tests/components/spi/test.esp32-p4-idf.yaml new file mode 100644 index 0000000000..061e3dd44a --- /dev/null +++ b/tests/components/spi/test.esp32-p4-idf.yaml @@ -0,0 +1,38 @@ +spi: + - id: quad_spi + type: quad + interface: spi3 + clk_pin: + number: 47 + data_pins: + - allow_other_uses: true + number: 40 + - allow_other_uses: true + number: 41 + - allow_other_uses: true + number: 42 + - allow_other_uses: true + number: 43 + - id: octal_spi + type: octal + interface: hardware + clk_pin: + number: 0 + data_pins: + - 36 + - 37 + - 38 + - 39 + - allow_other_uses: true + number: 40 + - allow_other_uses: true + number: 41 + - allow_other_uses: true + number: 42 + - allow_other_uses: true + number: 43 + - id: spi_id_3 + interface: any + clk_pin: 8 + mosi_pin: 9 + diff --git a/tests/components/sx1509/common.yaml b/tests/components/sx1509/common.yaml index a09d850649..a83217e579 100644 --- a/tests/components/sx1509/common.yaml +++ b/tests/components/sx1509/common.yaml @@ -6,6 +6,12 @@ i2c: sx1509: - id: sx1509_hub address: 0x3E + keypad: + key_rows: 2 + key_columns: 2 + keys: abcd + on_key: + - lambda: ESP_LOGD("test", "got key '%c'", x); binary_sensor: - platform: gpio @@ -13,6 +19,11 @@ binary_sensor: pin: sx1509: sx1509_hub number: 3 + - platform: sx1509 + sx1509_id: sx1509_hub + name: "keypadkey_0" + row: 0 + col: 0 switch: - platform: gpio diff --git a/tests/components/usb_host/test.esp32-s3-idf.yaml b/tests/components/usb_host/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..a2892872e5 --- /dev/null +++ b/tests/components/usb_host/test.esp32-s3-idf.yaml @@ -0,0 +1,5 @@ +usb_host: + devices: + - id: device_1 + vid: 0x1234 + pid: 0x1234 diff --git a/tests/components/usb_uart/common.yaml b/tests/components/usb_uart/common.yaml new file mode 100644 index 0000000000..46ad6291f9 --- /dev/null +++ b/tests/components/usb_uart/common.yaml @@ -0,0 +1,33 @@ +usb_uart: + - id: uart_0 + type: cdc_acm + vid: 0x1234 + pid: 0x5678 + channels: + - id: channel_0_1 + - id: uart_1 + type: cp210x + channels: + - id: channel_1_1 + baud_rate: 115200 + stop_bits: 2 + data_bits: 7 + parity: even + - id: uart_2 + type: ch34x + channels: + - id: channel_2_1 + baud_rate: 115200 + - id: channel_2_2 + baud_rate: 9600 + - id: uart_3 + type: ch340 + channels: + - id: channel_3_1 + baud_rate: 57600 + - id: uart_4 + type: esp_jtag + channels: + - id: channel_4_1 + debug: true + dummy_receiver: true diff --git a/tests/components/usb_uart/test.esp32-s3-idf.yaml b/tests/components/usb_uart/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/usb_uart/test.esp32-s3-idf.yaml @@ -0,0 +1 @@ +!include common.yaml diff --git a/tests/components/wk2132_i2c/common.yaml b/tests/components/wk2132_i2c/common.yaml index f9c8ab756d..942e01aafc 100644 --- a/tests/components/wk2132_i2c/common.yaml +++ b/tests/components/wk2132_i2c/common.yaml @@ -17,4 +17,10 @@ wk2132_i2c: parity: none - id: wk2132_id_1 channel: 1 - baud_rate: 19200 + baud_rate: 9600 + +# Ensures a sensor doesn't break validation +sensor: + - platform: a02yyuw + uart_id: wk2132_id_1 + id: distance_sensor diff --git a/tests/components/wk2132_i2c/test.esp32-ard.yaml b/tests/components/wk2132_i2c/test.esp32-ard.yaml index 3b761d3fc1..94552c5a40 100644 --- a/tests/components/wk2132_i2c/test.esp32-ard.yaml +++ b/tests/components/wk2132_i2c/test.esp32-ard.yaml @@ -3,3 +3,4 @@ substitutions: sda_pin: GPIO21 <<: !include common.yaml + diff --git a/tests/components/wk2132_spi/common.yaml b/tests/components/wk2132_spi/common.yaml index b21e89120c..a077b36998 100644 --- a/tests/components/wk2132_spi/common.yaml +++ b/tests/components/wk2132_spi/common.yaml @@ -18,4 +18,10 @@ wk2132_spi: parity: none - id: wk2132_spi_id1 channel: 1 - baud_rate: 921600 + baud_rate: 9600 + +# Ensures a sensor doesn't break validation +sensor: + - platform: a02yyuw + uart_id: wk2132_spi_id1 + id: distance_sensor diff --git a/tests/components/wk2168_i2c/common.yaml b/tests/components/wk2168_i2c/common.yaml index fe4689d6db..10463e8abf 100644 --- a/tests/components/wk2168_i2c/common.yaml +++ b/tests/components/wk2168_i2c/common.yaml @@ -24,7 +24,13 @@ wk2168_i2c: baud_rate: 115200 - id: id3 channel: 3 - baud_rate: 115200 + baud_rate: 9600 + +# Ensures a sensor doesn't break validation +sensor: + - platform: a02yyuw + uart_id: id3 + id: distance_sensor # individual binary_sensor inputs binary_sensor: diff --git a/tests/components/wk2168_spi/common.yaml b/tests/components/wk2168_spi/common.yaml index 7626e18df6..fb126193fc 100644 --- a/tests/components/wk2168_spi/common.yaml +++ b/tests/components/wk2168_spi/common.yaml @@ -24,7 +24,13 @@ wk2168_spi: baud_rate: 115200 - id: id3 channel: 3 - baud_rate: 115200 + baud_rate: 9600 + +# Ensures a sensor doesn't break validation +sensor: + - platform: a02yyuw + uart_id: id3 + id: distance_sensor # individual binary_sensor inputs binary_sensor: diff --git a/tests/components/wk2204_i2c/common.yaml b/tests/components/wk2204_i2c/common.yaml index 80f636c690..70c0f4babf 100644 --- a/tests/components/wk2204_i2c/common.yaml +++ b/tests/components/wk2204_i2c/common.yaml @@ -25,4 +25,10 @@ wk2204_i2c: parity: none - id: wk2204_id_3 channel: 3 - baud_rate: 19200 + baud_rate: 9600 + +# Ensures a sensor doesn't break validation +sensor: + - platform: a02yyuw + uart_id: wk2204_id_3 + id: distance_sensor diff --git a/tests/components/wk2204_spi/common.yaml b/tests/components/wk2204_spi/common.yaml index 3bae9c9a6d..a08cdb906f 100644 --- a/tests/components/wk2204_spi/common.yaml +++ b/tests/components/wk2204_spi/common.yaml @@ -26,4 +26,10 @@ wk2204_spi: parity: none - id: wk2204_spi_id3 channel: 3 - baud_rate: 921600 + baud_rate: 9600 + +# Ensures a sensor doesn't break validation +sensor: + - platform: a02yyuw + uart_id: wk2204_spi_id3 + id: distance_sensor diff --git a/tests/components/wk2212_i2c/common.yaml b/tests/components/wk2212_i2c/common.yaml index 2e891c5520..0759ef8688 100644 --- a/tests/components/wk2212_i2c/common.yaml +++ b/tests/components/wk2212_i2c/common.yaml @@ -18,10 +18,16 @@ wk2212_i2c: parity: none - id: uart_i2c_id1 channel: 1 - baud_rate: 115200 + baud_rate: 9600 stop_bits: 1 parity: none +# Ensures a sensor doesn't break validation +sensor: + - platform: a02yyuw + uart_id: uart_i2c_id1 + id: distance_sensor + # individual binary_sensor inputs binary_sensor: - platform: gpio diff --git a/tests/components/wk2212_spi/common.yaml b/tests/components/wk2212_spi/common.yaml index ad9f11d9e8..693d2a9ab2 100644 --- a/tests/components/wk2212_spi/common.yaml +++ b/tests/components/wk2212_spi/common.yaml @@ -18,7 +18,13 @@ wk2212_spi: parity: none - id: id1 channel: 1 - baud_rate: 115200 + baud_rate: 9600 + +# Ensures a sensor doesn't break validation +sensor: + - platform: a02yyuw + uart_id: id1 + id: distance_sensor # individual binary_sensor inputs binary_sensor: @@ -55,4 +61,3 @@ switch: mode: output: true inverted: true - diff --git a/tests/dashboard/test_web_server.py b/tests/dashboard/test_web_server.py index a61850abf3..cd02200d0b 100644 --- a/tests/dashboard/test_web_server.py +++ b/tests/dashboard/test_web_server.py @@ -53,6 +53,8 @@ async def dashboard() -> DashboardTestHelper: assert DASHBOARD.settings.on_ha_addon is True assert DASHBOARD.settings.using_auth is False task = asyncio.create_task(DASHBOARD.async_run()) + # Wait for initial device loading to complete + await DASHBOARD.entries.async_request_update_entries() client = AsyncHTTPClient() io_loop = IOLoop(make_current=False) yield DashboardTestHelper(io_loop, client, port) @@ -75,6 +77,7 @@ async def test_devices_page(dashboard: DashboardTestHelper) -> None: assert response.headers["content-type"] == "application/json" json_data = json.loads(response.body.decode()) configured_devices = json_data["configured"] + assert len(configured_devices) != 0 first_device = configured_devices[0] assert first_device["name"] == "pico" assert first_device["configuration"] == "pico.yaml" diff --git a/tests/integration/README.md b/tests/integration/README.md new file mode 100644 index 0000000000..26bd5a00ee --- /dev/null +++ b/tests/integration/README.md @@ -0,0 +1,80 @@ +# ESPHome Integration Tests + +This directory contains end-to-end integration tests for ESPHome, focusing on testing the complete flow from YAML configuration to running devices with API connections. + +## Structure + +- `conftest.py` - Common fixtures and utilities +- `const.py` - Constants used throughout the integration tests +- `types.py` - Type definitions for fixtures and functions +- `fixtures/` - YAML configuration files for tests +- `test_*.py` - Individual test files + +## How it works + +### Automatic YAML Loading + +The `yaml_config` fixture automatically loads YAML configurations based on the test name: +- It looks for a file named after the test function (e.g., `test_host_mode_basic` → `fixtures/host_mode_basic.yaml`) +- The fixture file must exist or the test will fail with a clear error message +- The fixture automatically injects a dynamic port number into the API configuration + +### Key Fixtures + +- `run_compiled` - Combines write, compile, and run operations into a single context manager +- `api_client_connected` - Creates an API client that automatically connects using ReconnectLogic +- `reserved_tcp_port` - Reserves a TCP port by holding the socket open until ESPHome needs it +- `unused_tcp_port` - Provides the reserved port number for each test + +### Writing Tests + +The simplest way to write a test is to use the `run_compiled` and `api_client_connected` fixtures: + +```python +@pytest.mark.asyncio +async def test_my_feature( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + # Write, compile and run the ESPHome device, then connect to API + async with run_compiled(yaml_config), api_client_connected() as client: + # Test your feature using the connected client + device_info = await client.device_info() + assert device_info is not None +``` + +### Creating YAML Fixtures + +Create a YAML file in the `fixtures/` directory with the same name as your test function (without the `test_` prefix): + +```yaml +# fixtures/my_feature.yaml +esphome: + name: my-test-device +host: +api: # Port will be automatically injected +logger: +# Add your components here +``` + +## Running Tests + +```bash +# Run all integration tests +script/integration_test + +# Run a specific test +pytest -vv tests/integration/test_host_mode_basic.py + +# Debug compilation errors or see ESPHome output +pytest -s tests/integration/test_host_mode_basic.py +``` + +## Implementation Details + +- Tests automatically wait for the API port to be available before connecting +- Process cleanup is handled automatically, with graceful shutdown using SIGINT +- Each test gets its own temporary directory and unique port +- Port allocation minimizes race conditions by holding the socket until just before ESPHome starts +- Output from ESPHome processes is displayed for debugging diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 0000000000..0cf87d2169 --- /dev/null +++ b/tests/integration/__init__.py @@ -0,0 +1,3 @@ +"""ESPHome integration tests.""" + +from __future__ import annotations diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py new file mode 100644 index 0000000000..90377300a6 --- /dev/null +++ b/tests/integration/conftest.py @@ -0,0 +1,556 @@ +"""Common fixtures for integration tests.""" + +from __future__ import annotations + +import asyncio +from collections.abc import AsyncGenerator, Generator +from contextlib import AbstractAsyncContextManager, asynccontextmanager +import logging +import os +from pathlib import Path +import platform +import signal +import socket +import sys +import tempfile +from typing import TextIO + +from aioesphomeapi import APIClient, APIConnectionError, LogParser, ReconnectLogic +import pytest +import pytest_asyncio + +import esphome.config +from esphome.core import CORE +from esphome.platformio_api import get_idedata + +from .const import ( + API_CONNECTION_TIMEOUT, + DEFAULT_API_PORT, + LOCALHOST, + PORT_POLL_INTERVAL, + PORT_WAIT_TIMEOUT, + SIGINT_TIMEOUT, + SIGTERM_TIMEOUT, +) +from .types import ( + APIClientConnectedFactory, + APIClientFactory, + CompileFunction, + ConfigWriter, + RunCompiledFunction, +) + +# Skip all integration tests on Windows +if platform.system() == "Windows": + pytest.skip( + "Integration tests are not supported on Windows", allow_module_level=True + ) + +import pty # not available on Windows + + +@pytest.fixture(scope="module", autouse=True) +def enable_aioesphomeapi_debug_logging(): + """Enable debug logging for aioesphomeapi to help diagnose connection issues.""" + # Get the aioesphomeapi logger + logger = logging.getLogger("aioesphomeapi") + # Save the original level + original_level = logger.level + # Set to DEBUG level + logger.setLevel(logging.DEBUG) + # Also ensure we have a handler that outputs to console + if not logger.handlers: + handler = logging.StreamHandler() + handler.setLevel(logging.DEBUG) + formatter = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + ) + handler.setFormatter(formatter) + logger.addHandler(handler) + yield + # Restore original level + logger.setLevel(original_level) + + +@pytest.fixture +def integration_test_dir() -> Generator[Path]: + """Create a temporary directory for integration tests.""" + with tempfile.TemporaryDirectory() as tmpdir: + yield Path(tmpdir) + + +@pytest.fixture +def reserved_tcp_port() -> Generator[tuple[int, socket.socket]]: + """Reserve an unused TCP port by holding the socket open.""" + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(("", 0)) + port = s.getsockname()[1] + try: + yield port, s + finally: + s.close() + + +@pytest.fixture +def unused_tcp_port(reserved_tcp_port: tuple[int, socket.socket]) -> int: + """Get the reserved TCP port number.""" + return reserved_tcp_port[0] + + +@pytest_asyncio.fixture +async def yaml_config(request: pytest.FixtureRequest, unused_tcp_port: int) -> str: + """Load YAML configuration based on test name.""" + # Get the test function name + test_name: str = request.node.name + # Extract the base test name (remove test_ prefix and any parametrization) + base_name = test_name.replace("test_", "").partition("[")[0] + + # Load the fixture file + fixture_path = Path(__file__).parent / "fixtures" / f"{base_name}.yaml" + if not fixture_path.exists(): + raise FileNotFoundError(f"Fixture file not found: {fixture_path}") + + loop = asyncio.get_running_loop() + content = await loop.run_in_executor(None, fixture_path.read_text) + + # Replace the port in the config if it contains api section + if "api:" in content: + # Add port configuration after api: + content = content.replace("api:", f"api:\n port: {unused_tcp_port}") + + # Add debug build flags for integration tests to enable assertions + if "esphome:" in content: + # Check if platformio_options already exists + if "platformio_options:" not in content: + # Add platformio_options with debug flags after esphome: + content = content.replace( + "esphome:", + "esphome:\n" + " # Enable assertions for integration tests\n" + " platformio_options:\n" + " build_flags:\n" + ' - "-DDEBUG" # Enable assert() statements\n' + ' - "-g" # Add debug symbols', + ) + + return content + + +@pytest_asyncio.fixture +async def write_yaml_config( + integration_test_dir: Path, request: pytest.FixtureRequest +) -> AsyncGenerator[ConfigWriter]: + """Write YAML configuration to a file.""" + # Get the test name for default filename + test_name = request.node.name + base_name = test_name.replace("test_", "").split("[")[0] + + async def _write_config(content: str, filename: str | None = None) -> Path: + if filename is None: + filename = f"{base_name}.yaml" + config_path = integration_test_dir / filename + loop = asyncio.get_running_loop() + await loop.run_in_executor(None, config_path.write_text, content) + return config_path + + yield _write_config + + +@pytest_asyncio.fixture +async def compile_esphome( + integration_test_dir: Path, +) -> AsyncGenerator[CompileFunction]: + """Compile an ESPHome configuration and return the binary path.""" + + async def _compile(config_path: Path) -> Path: + # Retry compilation up to 3 times if we get a segfault + max_retries = 3 + for attempt in range(max_retries): + # Compile using subprocess, inheriting stdout/stderr to show progress + proc = await asyncio.create_subprocess_exec( + "esphome", + "compile", + str(config_path), + cwd=integration_test_dir, + stdout=None, # Inherit stdout + stderr=None, # Inherit stderr + stdin=asyncio.subprocess.DEVNULL, + # Start in a new process group to isolate signal handling + start_new_session=True, + ) + await proc.wait() + + if proc.returncode == 0: + # Success! + break + elif proc.returncode == -11 and attempt < max_retries - 1: + # Segfault (-11 = SIGSEGV), retry + print( + f"Compilation segfaulted (attempt {attempt + 1}/{max_retries}), retrying..." + ) + await asyncio.sleep(1) # Brief pause before retry + continue + else: + # Other error or final retry + raise RuntimeError( + f"Failed to compile {config_path}, return code: {proc.returncode}. " + f"Run with 'pytest -s' to see compilation output." + ) + + # Load the config to get idedata (blocking call, must use executor) + loop = asyncio.get_running_loop() + + def _read_config_and_get_binary(): + CORE.config_path = str(config_path) + config = esphome.config.read_config( + {"command": "compile", "config": str(config_path)} + ) + if config is None: + raise RuntimeError(f"Failed to read config from {config_path}") + + # Get the compiled binary path + idedata = get_idedata(config) + return Path(idedata.firmware_elf_path) + + binary_path = await loop.run_in_executor(None, _read_config_and_get_binary) + + if not binary_path.exists(): + raise RuntimeError(f"Compiled binary not found at {binary_path}") + + return binary_path + + yield _compile + + +@asynccontextmanager +async def create_api_client( + address: str = LOCALHOST, + port: int = DEFAULT_API_PORT, + password: str = "", + noise_psk: str | None = None, + client_info: str = "integration-test", +) -> AsyncGenerator[APIClient]: + """Create an API client context manager.""" + client = APIClient( + address=address, + port=port, + password=password, + noise_psk=noise_psk, + client_info=client_info, + ) + try: + yield client + finally: + await client.disconnect() + + +@pytest_asyncio.fixture +async def api_client_factory( + unused_tcp_port: int, +) -> AsyncGenerator[APIClientFactory]: + """Factory for creating API client context managers.""" + + def _create_client( + address: str = LOCALHOST, + port: int | None = None, + password: str = "", + noise_psk: str | None = None, + client_info: str = "integration-test", + ) -> AbstractAsyncContextManager[APIClient]: + return create_api_client( + address=address, + port=port if port is not None else unused_tcp_port, + password=password, + noise_psk=noise_psk, + client_info=client_info, + ) + + yield _create_client + + +@asynccontextmanager +async def wait_and_connect_api_client( + address: str = LOCALHOST, + port: int = DEFAULT_API_PORT, + password: str = "", + noise_psk: str | None = None, + client_info: str = "integration-test", + timeout: float = API_CONNECTION_TIMEOUT, +) -> AsyncGenerator[APIClient]: + """Wait for API to be available and connect.""" + client = APIClient( + address=address, + port=port, + password=password, + noise_psk=noise_psk, + client_info=client_info, + ) + + # Create a future to signal when connected + loop = asyncio.get_running_loop() + connected_future: asyncio.Future[None] = loop.create_future() + + async def on_connect() -> None: + """Called when successfully connected.""" + if not connected_future.done(): + connected_future.set_result(None) + + async def on_disconnect(expected_disconnect: bool) -> None: + """Called when disconnected.""" + if not connected_future.done() and not expected_disconnect: + connected_future.set_exception( + APIConnectionError("Disconnected before fully connected") + ) + + async def on_connect_error(err: Exception) -> None: + """Called when connection fails.""" + if not connected_future.done(): + connected_future.set_exception(err) + + # Create and start the reconnect logic + reconnect_logic = ReconnectLogic( + client=client, + on_connect=on_connect, + on_disconnect=on_disconnect, + zeroconf_instance=None, # Not using zeroconf for integration tests + name=f"{address}:{port}", + on_connect_error=on_connect_error, + ) + + try: + # Start the connection + await reconnect_logic.start() + + # Wait for connection with timeout + try: + await asyncio.wait_for(connected_future, timeout=timeout) + except asyncio.TimeoutError: + raise TimeoutError(f"Failed to connect to API after {timeout} seconds") + + yield client + finally: + # Stop reconnect logic and disconnect + await reconnect_logic.stop() + await client.disconnect() + + +@pytest_asyncio.fixture +async def api_client_connected( + unused_tcp_port: int, +) -> AsyncGenerator[APIClientConnectedFactory]: + """Factory for creating connected API client context managers.""" + + def _connect_client( + address: str = LOCALHOST, + port: int | None = None, + password: str = "", + noise_psk: str | None = None, + client_info: str = "integration-test", + timeout: float = API_CONNECTION_TIMEOUT, + ) -> AbstractAsyncContextManager[APIClient]: + return wait_and_connect_api_client( + address=address, + port=port if port is not None else unused_tcp_port, + password=password, + noise_psk=noise_psk, + client_info=client_info, + timeout=timeout, + ) + + yield _connect_client + + +async def _read_stream_lines( + stream: asyncio.StreamReader, lines: list[str], output_stream: TextIO +) -> None: + """Read lines from a stream, append to list, and echo to output stream.""" + log_parser = LogParser() + while line := await stream.readline(): + decoded_line = ( + line.replace(b"\r", b"") + .replace(b"\n", b"") + .decode("utf8", "backslashreplace") + ) + lines.append(decoded_line.rstrip()) + # Echo to stdout/stderr in real-time + # Print without newline to avoid double newlines + print( + log_parser.parse_line(decoded_line, timestamp=""), + file=output_stream, + flush=True, + ) + + +@asynccontextmanager +async def run_binary_and_wait_for_port( + binary_path: Path, + host: str, + port: int, + timeout: float = PORT_WAIT_TIMEOUT, +) -> AsyncGenerator[None]: + """Run a binary, wait for it to open a port, and clean up on exit.""" + # Create a pseudo-terminal to make the binary think it's running interactively + # This is needed because the ESPHome host logger checks isatty() + controller_fd, device_fd = pty.openpty() + + # Run the compiled binary with PTY + process = await asyncio.create_subprocess_exec( + str(binary_path), + stdout=device_fd, + stderr=device_fd, + stdin=asyncio.subprocess.DEVNULL, + # Start in a new process group to isolate signal handling + start_new_session=True, + pass_fds=(device_fd,), + ) + + # Close the device end in the parent process + os.close(device_fd) + + # Convert controller_fd to async streams for reading + loop = asyncio.get_running_loop() + controller_reader = asyncio.StreamReader() + controller_protocol = asyncio.StreamReaderProtocol(controller_reader) + controller_transport, _ = await loop.connect_read_pipe( + lambda: controller_protocol, os.fdopen(controller_fd, "rb", 0) + ) + output_reader = controller_reader + + if process.returncode is not None: + raise RuntimeError( + f"Process died immediately with return code {process.returncode}. " + "Ensure the binary is valid and can run successfully." + ) + + # Wait for the API server to start listening + loop = asyncio.get_running_loop() + start_time = loop.time() + + # Start collecting output + stdout_lines: list[str] = [] + output_tasks: list[asyncio.Task] = [] + + try: + # Read from output stream + output_tasks = [ + asyncio.create_task( + _read_stream_lines(output_reader, stdout_lines, sys.stdout) + ) + ] + + # Small yield to ensure the process has a chance to start + await asyncio.sleep(0) + + while loop.time() - start_time < timeout: + try: + # Try to connect to the port + _, writer = await asyncio.open_connection(host, port) + writer.close() + await writer.wait_closed() + # Port is open, yield control + yield + return + except (ConnectionRefusedError, OSError): + # Check if process died + if process.returncode is not None: + break + # Port not open yet, wait a bit and try again + await asyncio.sleep(PORT_POLL_INTERVAL) + + # Timeout or process died - build error message + error_msg = f"Port {port} on {host} did not open within {timeout} seconds" + + if process.returncode is not None: + error_msg += f"\nProcess exited with code: {process.returncode}" + + # Include any output collected so far + if stdout_lines: + error_msg += "\n\n--- Process Output ---\n" + error_msg += "\n".join(stdout_lines[-100:]) # Last 100 lines + + raise TimeoutError(error_msg) + + finally: + # Cancel output collection tasks + for task in output_tasks: + task.cancel() + # Wait for tasks to complete and check for exceptions + results = await asyncio.gather(*output_tasks, return_exceptions=True) + for i, result in enumerate(results): + if isinstance(result, Exception) and not isinstance( + result, asyncio.CancelledError + ): + print( + f"Error reading from PTY: {result}", + file=sys.stderr, + ) + + # Close the PTY transport (Unix only) + if controller_transport is not None: + controller_transport.close() + + # Cleanup: terminate the process gracefully + if process.returncode is None: + # Send SIGINT (Ctrl+C) for graceful shutdown + process.send_signal(signal.SIGINT) + try: + await asyncio.wait_for(process.wait(), timeout=SIGINT_TIMEOUT) + except asyncio.TimeoutError: + # If SIGINT didn't work, try SIGTERM + process.terminate() + try: + await asyncio.wait_for(process.wait(), timeout=SIGTERM_TIMEOUT) + except asyncio.TimeoutError: + # Last resort: SIGKILL + process.kill() + await process.wait() + + +@asynccontextmanager +async def run_compiled_context( + yaml_content: str, + filename: str | None, + write_yaml_config: ConfigWriter, + compile_esphome: CompileFunction, + port: int, + port_socket: socket.socket | None = None, +) -> AsyncGenerator[None]: + """Context manager to write, compile and run an ESPHome configuration.""" + # Write the YAML config + config_path = await write_yaml_config(yaml_content, filename) + + # Compile the configuration and get binary path + binary_path = await compile_esphome(config_path) + + # Close the port socket right before running to release the port + if port_socket is not None: + port_socket.close() + + # Run the binary and wait for the API server to start + async with run_binary_and_wait_for_port(binary_path, LOCALHOST, port): + yield + + +@pytest_asyncio.fixture +async def run_compiled( + write_yaml_config: ConfigWriter, + compile_esphome: CompileFunction, + reserved_tcp_port: tuple[int, socket.socket], +) -> AsyncGenerator[RunCompiledFunction]: + """Write, compile and run an ESPHome configuration.""" + port, port_socket = reserved_tcp_port + + def _run_compiled( + yaml_content: str, filename: str | None = None + ) -> AbstractAsyncContextManager[asyncio.subprocess.Process]: + return run_compiled_context( + yaml_content, + filename, + write_yaml_config, + compile_esphome, + port, + port_socket, + ) + + yield _run_compiled diff --git a/tests/integration/const.py b/tests/integration/const.py new file mode 100644 index 0000000000..6876bbd443 --- /dev/null +++ b/tests/integration/const.py @@ -0,0 +1,14 @@ +"""Constants for integration tests.""" + +# Network constants +DEFAULT_API_PORT = 6053 +LOCALHOST = "127.0.0.1" + +# Timeout constants +API_CONNECTION_TIMEOUT = 30.0 # seconds +PORT_WAIT_TIMEOUT = 30.0 # seconds +PORT_POLL_INTERVAL = 0.1 # seconds + +# Process shutdown timeouts +SIGINT_TIMEOUT = 5.0 # seconds +SIGTERM_TIMEOUT = 2.0 # seconds diff --git a/tests/integration/fixtures/api_message_size_batching.yaml b/tests/integration/fixtures/api_message_size_batching.yaml new file mode 100644 index 0000000000..c730dc1aa3 --- /dev/null +++ b/tests/integration/fixtures/api_message_size_batching.yaml @@ -0,0 +1,161 @@ +esphome: + name: message-size-batching-test +host: +api: +# Default batch_delay to test batching +logger: + +# Create entities that will produce different protobuf header sizes +# Header size depends on: 1 byte indicator + varint(payload_size) + varint(message_type) +# 4-byte header: type < 128, payload < 128 +# 5-byte header: type < 128, payload 128-16383 OR type 128+, payload < 128 +# 6-byte header: type 128+, payload 128-16383 + +# Small select with few options - produces small message +select: + - platform: template + name: "Small Select" + id: small_select + optimistic: true + options: + - "Option A" + - "Option B" + initial_option: "Option A" + update_interval: 5.0s + + # Medium select with more options - produces medium message + - platform: template + name: "Medium Select" + id: medium_select + optimistic: true + options: + - "Option 001" + - "Option 002" + - "Option 003" + - "Option 004" + - "Option 005" + - "Option 006" + - "Option 007" + - "Option 008" + - "Option 009" + - "Option 010" + - "Option 011" + - "Option 012" + - "Option 013" + - "Option 014" + - "Option 015" + - "Option 016" + - "Option 017" + - "Option 018" + - "Option 019" + - "Option 020" + initial_option: "Option 001" + update_interval: 5.0s + + # Large select with many options - produces larger message + - platform: template + name: "Large Select with Many Options to Create Larger Payload" + id: large_select + optimistic: true + options: + - "Long Option Name 001 - This is a longer option name to increase message size" + - "Long Option Name 002 - This is a longer option name to increase message size" + - "Long Option Name 003 - This is a longer option name to increase message size" + - "Long Option Name 004 - This is a longer option name to increase message size" + - "Long Option Name 005 - This is a longer option name to increase message size" + - "Long Option Name 006 - This is a longer option name to increase message size" + - "Long Option Name 007 - This is a longer option name to increase message size" + - "Long Option Name 008 - This is a longer option name to increase message size" + - "Long Option Name 009 - This is a longer option name to increase message size" + - "Long Option Name 010 - This is a longer option name to increase message size" + - "Long Option Name 011 - This is a longer option name to increase message size" + - "Long Option Name 012 - This is a longer option name to increase message size" + - "Long Option Name 013 - This is a longer option name to increase message size" + - "Long Option Name 014 - This is a longer option name to increase message size" + - "Long Option Name 015 - This is a longer option name to increase message size" + - "Long Option Name 016 - This is a longer option name to increase message size" + - "Long Option Name 017 - This is a longer option name to increase message size" + - "Long Option Name 018 - This is a longer option name to increase message size" + - "Long Option Name 019 - This is a longer option name to increase message size" + - "Long Option Name 020 - This is a longer option name to increase message size" + - "Long Option Name 021 - This is a longer option name to increase message size" + - "Long Option Name 022 - This is a longer option name to increase message size" + - "Long Option Name 023 - This is a longer option name to increase message size" + - "Long Option Name 024 - This is a longer option name to increase message size" + - "Long Option Name 025 - This is a longer option name to increase message size" + - "Long Option Name 026 - This is a longer option name to increase message size" + - "Long Option Name 027 - This is a longer option name to increase message size" + - "Long Option Name 028 - This is a longer option name to increase message size" + - "Long Option Name 029 - This is a longer option name to increase message size" + - "Long Option Name 030 - This is a longer option name to increase message size" + - "Long Option Name 031 - This is a longer option name to increase message size" + - "Long Option Name 032 - This is a longer option name to increase message size" + - "Long Option Name 033 - This is a longer option name to increase message size" + - "Long Option Name 034 - This is a longer option name to increase message size" + - "Long Option Name 035 - This is a longer option name to increase message size" + - "Long Option Name 036 - This is a longer option name to increase message size" + - "Long Option Name 037 - This is a longer option name to increase message size" + - "Long Option Name 038 - This is a longer option name to increase message size" + - "Long Option Name 039 - This is a longer option name to increase message size" + - "Long Option Name 040 - This is a longer option name to increase message size" + - "Long Option Name 041 - This is a longer option name to increase message size" + - "Long Option Name 042 - This is a longer option name to increase message size" + - "Long Option Name 043 - This is a longer option name to increase message size" + - "Long Option Name 044 - This is a longer option name to increase message size" + - "Long Option Name 045 - This is a longer option name to increase message size" + - "Long Option Name 046 - This is a longer option name to increase message size" + - "Long Option Name 047 - This is a longer option name to increase message size" + - "Long Option Name 048 - This is a longer option name to increase message size" + - "Long Option Name 049 - This is a longer option name to increase message size" + - "Long Option Name 050 - This is a longer option name to increase message size" + initial_option: "Long Option Name 001 - This is a longer option name to increase message size" + update_interval: 5.0s + +# Text sensors with different value lengths +text_sensor: + - platform: template + name: "Short Text Sensor" + id: short_text_sensor + lambda: |- + return {"OK"}; + update_interval: 5.0s + + - platform: template + name: "Medium Text Sensor" + id: medium_text_sensor + lambda: |- + return {"This is a medium length text sensor value that should produce a medium sized message"}; + update_interval: 5.0s + + - platform: template + name: "Long Text Sensor with Very Long Value" + id: long_text_sensor + lambda: |- + return {"This is a very long text sensor value that contains a lot of text to ensure we get a larger protobuf message. The message should be long enough to require a 2-byte varint for the payload size, which happens when the payload exceeds 127 bytes. Let's add even more text here to make sure we exceed that threshold and test the batching of messages with different header sizes properly."}; + update_interval: 5.0s + +# Text input which can have various lengths +text: + - platform: template + name: "Test Text Input" + id: test_text_input + optimistic: true + mode: text + min_length: 0 + max_length: 255 + initial_value: "Initial value" + update_interval: 5.0s + +# Number entity to add variety (different message type number) +# The ListEntitiesNumberResponse has message type 49 +# The NumberStateResponse has message type 50 +number: + - platform: template + name: "Test Number with Long Name to Increase Message Size" + id: test_number + optimistic: true + min_value: 0 + max_value: 1000 + step: 0.1 + initial_value: 42.0 + update_interval: 5.0s diff --git a/tests/integration/fixtures/host_mode_basic.yaml b/tests/integration/fixtures/host_mode_basic.yaml new file mode 100644 index 0000000000..9bcda57f4f --- /dev/null +++ b/tests/integration/fixtures/host_mode_basic.yaml @@ -0,0 +1,5 @@ +esphome: + name: host-test +host: +api: +logger: diff --git a/tests/integration/fixtures/host_mode_batch_delay.yaml b/tests/integration/fixtures/host_mode_batch_delay.yaml new file mode 100644 index 0000000000..8abe1da6fb --- /dev/null +++ b/tests/integration/fixtures/host_mode_batch_delay.yaml @@ -0,0 +1,55 @@ +esphome: + name: host-batch-delay-test +host: +api: + batch_delay: 0ms +logger: + +# Add multiple sensors to test batching +sensor: + - platform: template + name: "Test Sensor 1" + id: test_sensor1 + lambda: |- + return 1.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 2" + id: test_sensor2 + lambda: |- + return 2.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 3" + id: test_sensor3 + lambda: |- + return 3.0; + update_interval: 0.1s + +binary_sensor: + - platform: template + name: "Test Binary Sensor 1" + id: test_binary_sensor1 + lambda: |- + return millis() % 1000 < 500; + - platform: template + name: "Test Binary Sensor 2" + id: test_binary_sensor2 + lambda: |- + return millis() % 2000 < 1000; + +switch: + - platform: template + name: "Test Switch 1" + id: test_switch1 + turn_on_action: + - logger.log: "Switch 1 turned on" + turn_off_action: + - logger.log: "Switch 1 turned off" + - platform: template + name: "Test Switch 2" + id: test_switch2 + turn_on_action: + - logger.log: "Switch 2 turned on" + turn_off_action: + - logger.log: "Switch 2 turned off" diff --git a/tests/integration/fixtures/host_mode_empty_string_options.yaml b/tests/integration/fixtures/host_mode_empty_string_options.yaml new file mode 100644 index 0000000000..ab8e6cd005 --- /dev/null +++ b/tests/integration/fixtures/host_mode_empty_string_options.yaml @@ -0,0 +1,58 @@ +esphome: + name: host-empty-string-test + +host: + +api: + batch_delay: 50ms + +select: + - platform: template + name: "Select Empty First" + id: select_empty_first + optimistic: true + options: + - "" # Empty string at the beginning + - "Option A" + - "Option B" + - "Option C" + initial_option: "Option A" + + - platform: template + name: "Select Empty Middle" + id: select_empty_middle + optimistic: true + options: + - "Option 1" + - "Option 2" + - "" # Empty string in the middle + - "Option 3" + - "Option 4" + initial_option: "Option 1" + + - platform: template + name: "Select Empty Last" + id: select_empty_last + optimistic: true + options: + - "Choice X" + - "Choice Y" + - "Choice Z" + - "" # Empty string at the end + initial_option: "Choice X" + +# Add a sensor to ensure we have other entities in the list +sensor: + - platform: template + name: "Test Sensor" + id: test_sensor + lambda: |- + return 42.0; + update_interval: 60s + +binary_sensor: + - platform: template + name: "Test Binary Sensor" + id: test_binary_sensor + lambda: |- + return true; diff --git a/tests/integration/fixtures/host_mode_entity_fields.yaml b/tests/integration/fixtures/host_mode_entity_fields.yaml new file mode 100644 index 0000000000..0bd87ee794 --- /dev/null +++ b/tests/integration/fixtures/host_mode_entity_fields.yaml @@ -0,0 +1,108 @@ +esphome: + name: host-test + +host: + +api: + +logger: + +# Test various entity types with different flag combinations +sensor: + - platform: template + name: "Test Normal Sensor" + id: normal_sensor + update_interval: 1s + lambda: |- + return 42.0; + + - platform: template + name: "Test Internal Sensor" + id: internal_sensor + internal: true + update_interval: 1s + lambda: |- + return 43.0; + + - platform: template + name: "Test Disabled Sensor" + id: disabled_sensor + disabled_by_default: true + update_interval: 1s + lambda: |- + return 44.0; + + - platform: template + name: "Test Mixed Flags Sensor" + id: mixed_flags_sensor + internal: true + entity_category: diagnostic + update_interval: 1s + lambda: |- + return 45.0; + + - platform: template + name: "Test Diagnostic Sensor" + id: diagnostic_sensor + entity_category: diagnostic + update_interval: 1s + lambda: |- + return 46.0; + + - platform: template + name: "Test All Flags Sensor" + id: all_flags_sensor + internal: true + disabled_by_default: true + entity_category: diagnostic + update_interval: 1s + lambda: |- + return 47.0; + +# Also test other entity types to ensure bit-packing works across all +binary_sensor: + - platform: template + name: "Test Binary Sensor" + entity_category: config + lambda: |- + return true; + +text_sensor: + - platform: template + name: "Test Text Sensor" + disabled_by_default: true + lambda: |- + return {"Hello"}; + +number: + - platform: template + name: "Test Number" + initial_value: 50 + min_value: 0 + max_value: 100 + step: 1 + optimistic: true + entity_category: diagnostic + +select: + - platform: template + name: "Test Select" + options: + - "Option 1" + - "Option 2" + initial_option: "Option 1" + optimistic: true + internal: true + +switch: + - platform: template + name: "Test Switch" + optimistic: true + disabled_by_default: true + entity_category: config + +button: + - platform: template + name: "Test Button" + on_press: + - logger.log: "Button pressed" diff --git a/tests/integration/fixtures/host_mode_fan_preset.yaml b/tests/integration/fixtures/host_mode_fan_preset.yaml new file mode 100644 index 0000000000..003f4a7760 --- /dev/null +++ b/tests/integration/fixtures/host_mode_fan_preset.yaml @@ -0,0 +1,34 @@ +esphome: + name: host-test + +host: + +api: + +logger: + +# Test fan with preset modes and speed settings +fan: + - platform: template + name: "Test Fan with Presets" + id: test_fan_presets + speed_count: 5 + preset_modes: + - "Eco" + - "Sleep" + - "Turbo" + has_oscillating: true + has_direction: true + + - platform: template + name: "Test Fan Simple" + id: test_fan_simple + speed_count: 3 + has_oscillating: false + has_direction: false + + - platform: template + name: "Test Fan No Speed" + id: test_fan_no_speed + has_oscillating: true + has_direction: false diff --git a/tests/integration/fixtures/host_mode_many_entities.yaml b/tests/integration/fixtures/host_mode_many_entities.yaml new file mode 100644 index 0000000000..3d1aa36196 --- /dev/null +++ b/tests/integration/fixtures/host_mode_many_entities.yaml @@ -0,0 +1,322 @@ +esphome: + name: host-mode-many-entities + friendly_name: "Host Mode Many Entities Test" + +logger: + +host: + +api: + +sensor: + # 50 test sensors with predictable values for batching test + - platform: template + name: "Test Sensor 1" + lambda: return 1.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 2" + lambda: return 2.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 3" + lambda: return 3.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 4" + lambda: return 4.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 5" + lambda: return 5.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 6" + lambda: return 6.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 7" + lambda: return 7.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 8" + lambda: return 8.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 9" + lambda: return 9.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 10" + lambda: return 10.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 11" + lambda: return 11.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 12" + lambda: return 12.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 13" + lambda: return 13.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 14" + lambda: return 14.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 15" + lambda: return 15.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 16" + lambda: return 16.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 17" + lambda: return 17.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 18" + lambda: return 18.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 19" + lambda: return 19.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 20" + lambda: return 20.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 21" + lambda: return 21.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 22" + lambda: return 22.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 23" + lambda: return 23.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 24" + lambda: return 24.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 25" + lambda: return 25.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 26" + lambda: return 26.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 27" + lambda: return 27.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 28" + lambda: return 28.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 29" + lambda: return 29.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 30" + lambda: return 30.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 31" + lambda: return 31.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 32" + lambda: return 32.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 33" + lambda: return 33.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 34" + lambda: return 34.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 35" + lambda: return 35.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 36" + lambda: return 36.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 37" + lambda: return 37.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 38" + lambda: return 38.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 39" + lambda: return 39.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 40" + lambda: return 40.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 41" + lambda: return 41.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 42" + lambda: return 42.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 43" + lambda: return 43.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 44" + lambda: return 44.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 45" + lambda: return 45.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 46" + lambda: return 46.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 47" + lambda: return 47.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 48" + lambda: return 48.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 49" + lambda: return 49.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 50" + lambda: return 50.0; + update_interval: 0.1s + +# Mixed entity types for comprehensive batching test +binary_sensor: + - platform: template + name: "Test Binary Sensor 1" + lambda: return millis() % 1000 < 500; + - platform: template + name: "Test Binary Sensor 2" + lambda: return millis() % 2000 < 1000; + +switch: + - platform: template + name: "Test Switch 1" + lambda: return true; + turn_on_action: + - logger.log: "Switch 1 ON" + turn_off_action: + - logger.log: "Switch 1 OFF" + - platform: template + name: "Test Switch 2" + lambda: return false; + turn_on_action: + - logger.log: "Switch 2 ON" + turn_off_action: + - logger.log: "Switch 2 OFF" + +text_sensor: + - platform: template + name: "Test Text Sensor 1" + lambda: return std::string("Test Value 1"); + - platform: template + name: "Test Text Sensor 2" + lambda: return std::string("Test Value 2"); + - platform: version + name: "ESPHome Version" + +number: + - platform: template + name: "Test Number" + min_value: 0 + max_value: 100 + step: 1 + lambda: return 50.0; + set_action: + - logger.log: "Number set" + +select: + - platform: template + name: "Test Select" + options: + - "Option 1" + - "Option 2" + initial_option: "Option 1" + optimistic: true + set_action: + - logger.log: "Select changed" + +text: + - platform: template + name: "Test Text" + mode: text + initial_value: "Hello" + set_action: + - logger.log: "Text changed" + +valve: + - platform: template + name: "Test Valve" + open_action: + - logger.log: "Valve opening" + close_action: + - logger.log: "Valve closing" + stop_action: + - logger.log: "Valve stopping" + +alarm_control_panel: + - platform: template + name: "Test Alarm" + codes: + - "1234" + arming_away_time: 0s + arming_home_time: 0s + pending_time: 0s + trigger_time: 300s + restore_mode: ALWAYS_DISARMED + on_disarmed: + - logger.log: "Alarm disarmed" + on_arming: + - logger.log: "Alarm arming" + on_armed_away: + - logger.log: "Alarm armed away" + on_armed_home: + - logger.log: "Alarm armed home" + on_pending: + - logger.log: "Alarm pending" + on_triggered: + - logger.log: "Alarm triggered" + +event: + - platform: template + name: "Test Event" + event_types: + - first_event + - second_event + +button: + - platform: template + name: "Test Button" + on_press: + - logger.log: "Button pressed" diff --git a/tests/integration/fixtures/host_mode_many_entities_multiple_connections.yaml b/tests/integration/fixtures/host_mode_many_entities_multiple_connections.yaml new file mode 100644 index 0000000000..296cb2455f --- /dev/null +++ b/tests/integration/fixtures/host_mode_many_entities_multiple_connections.yaml @@ -0,0 +1,136 @@ +esphome: + name: host-mode-many-entities-multi + friendly_name: "Host Mode Many Entities Multiple Connections Test" + +logger: + +host: + +api: + +sensor: + # 20 test sensors for faster testing with multiple connections + - platform: template + name: "Test Sensor 1" + lambda: return 1.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 2" + lambda: return 2.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 3" + lambda: return 3.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 4" + lambda: return 4.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 5" + lambda: return 5.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 6" + lambda: return 6.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 7" + lambda: return 7.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 8" + lambda: return 8.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 9" + lambda: return 9.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 10" + lambda: return 10.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 11" + lambda: return 11.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 12" + lambda: return 12.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 13" + lambda: return 13.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 14" + lambda: return 14.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 15" + lambda: return 15.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 16" + lambda: return 16.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 17" + lambda: return 17.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 18" + lambda: return 18.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 19" + lambda: return 19.0; + update_interval: 0.1s + - platform: template + name: "Test Sensor 20" + lambda: return 20.0; + update_interval: 0.1s + +# Mixed entity types for comprehensive batching test +binary_sensor: + - platform: template + name: "Test Binary Sensor 1" + lambda: return millis() % 1000 < 500; + - platform: template + name: "Test Binary Sensor 2" + lambda: return millis() % 2000 < 1000; + +text_sensor: + - platform: template + name: "Test Text Sensor 1" + lambda: return std::string("Test Value 1"); + - platform: template + name: "Test Text Sensor 2" + lambda: return std::string("Test Value 2"); + - platform: version + name: "ESPHome Version" + +switch: + - platform: template + name: "Test Switch 1" + lambda: return true; + turn_on_action: + - logger.log: "Switch 1 ON" + turn_off_action: + - logger.log: "Switch 1 OFF" + +button: + - platform: template + name: "Test Button" + on_press: + - logger.log: "Button pressed" + +number: + - platform: template + name: "Test Number" + min_value: 0 + max_value: 100 + step: 1 + lambda: return 50.0; + set_action: + - logger.log: "Number set" diff --git a/tests/integration/fixtures/host_mode_noise_encryption.yaml b/tests/integration/fixtures/host_mode_noise_encryption.yaml new file mode 100644 index 0000000000..da817fdc3b --- /dev/null +++ b/tests/integration/fixtures/host_mode_noise_encryption.yaml @@ -0,0 +1,50 @@ +esphome: + name: host-noise-test +host: +api: + encryption: + key: N4Yle5YirwZhPiHHsdZLdOA73ndj/84veVaLhTvxCuU= +logger: + +# Test sensors to verify batching works with noise encryption +sensor: + - platform: template + name: "Noise Test Sensor 1" + lambda: return 1.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 2" + lambda: return 2.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 3" + lambda: return 3.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 4" + lambda: return 4.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 5" + lambda: return 5.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 6" + lambda: return 6.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 7" + lambda: return 7.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 8" + lambda: return 8.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 9" + lambda: return 9.0; + update_interval: 2s + - platform: template + name: "Noise Test Sensor 10" + lambda: return 10.0; + update_interval: 2s diff --git a/tests/integration/fixtures/host_mode_noise_encryption_wrong_key.yaml b/tests/integration/fixtures/host_mode_noise_encryption_wrong_key.yaml new file mode 100644 index 0000000000..83605e28a3 --- /dev/null +++ b/tests/integration/fixtures/host_mode_noise_encryption_wrong_key.yaml @@ -0,0 +1,7 @@ +esphome: + name: host-noise-test +host: +api: + encryption: + key: N4Yle5YirwZhPiHHsdZLdOA73ndj/84veVaLhTvxCuU= +logger: diff --git a/tests/integration/fixtures/host_mode_reconnect.yaml b/tests/integration/fixtures/host_mode_reconnect.yaml new file mode 100644 index 0000000000..f240e4b2fe --- /dev/null +++ b/tests/integration/fixtures/host_mode_reconnect.yaml @@ -0,0 +1,5 @@ +esphome: + name: host-reconnect-test +host: +api: +logger: diff --git a/tests/integration/fixtures/host_mode_with_sensor.yaml b/tests/integration/fixtures/host_mode_with_sensor.yaml new file mode 100644 index 0000000000..fecd0b435b --- /dev/null +++ b/tests/integration/fixtures/host_mode_with_sensor.yaml @@ -0,0 +1,12 @@ +esphome: + name: host-sensor-test +host: +api: +logger: +sensor: + - platform: template + name: Test Sensor + id: test_sensor + unit_of_measurement: °C + lambda: return 42.0; + update_interval: 0.1s diff --git a/tests/integration/fixtures/large_message_batching.yaml b/tests/integration/fixtures/large_message_batching.yaml new file mode 100644 index 0000000000..1b2d817cd4 --- /dev/null +++ b/tests/integration/fixtures/large_message_batching.yaml @@ -0,0 +1,137 @@ +esphome: + name: large-message-test +host: +api: +logger: + +# Create a select entity with many options to exceed 1390 bytes +select: + - platform: template + name: "Large Select" + id: large_select + optimistic: true + options: + - "Option 000 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 001 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 002 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 003 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 004 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 005 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 006 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 007 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 008 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 009 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 010 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 011 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 012 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 013 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 014 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 015 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 016 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 017 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 018 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 019 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 020 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 021 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 022 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 023 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 024 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 025 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 026 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 027 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 028 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 029 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 030 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 031 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 032 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 033 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 034 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 035 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 036 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 037 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 038 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 039 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 040 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 041 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 042 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 043 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 044 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 045 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 046 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 047 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 048 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 049 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 050 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 051 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 052 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 053 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 054 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 055 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 056 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 057 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 058 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 059 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 060 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 061 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 062 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 063 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 064 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 065 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 066 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 067 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 068 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 069 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 070 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 071 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 072 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 073 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 074 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 075 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 076 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 077 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 078 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 079 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 080 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 081 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 082 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 083 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 084 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 085 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 086 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 087 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 088 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 089 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 090 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 091 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 092 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 093 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 094 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 095 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 096 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 097 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 098 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + - "Option 099 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + initial_option: "Option 000 - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + +# Add some other entities to test batching with the large select +sensor: + - platform: template + name: "Test Sensor" + id: test_sensor + lambda: |- + return 42.0; + update_interval: 1s + +binary_sensor: + - platform: template + name: "Test Binary Sensor" + id: test_binary_sensor + lambda: |- + return true; + +switch: + - platform: template + name: "Test Switch" + id: test_switch + optimistic: true + diff --git a/tests/integration/test_api_message_size_batching.py b/tests/integration/test_api_message_size_batching.py new file mode 100644 index 0000000000..631e64825e --- /dev/null +++ b/tests/integration/test_api_message_size_batching.py @@ -0,0 +1,194 @@ +"""Integration test for API batching with various message sizes.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import EntityState, NumberInfo, SelectInfo, TextInfo, TextSensorInfo +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_api_message_size_batching( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test API can batch messages of various sizes correctly.""" + # Write, compile and run the ESPHome device, then connect to API + loop = asyncio.get_running_loop() + async with run_compiled(yaml_config), api_client_connected() as client: + # Verify we can get device info + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "message-size-batching-test" + + # List entities - this will batch various sized messages together + entity_info, services = await asyncio.wait_for( + client.list_entities_services(), timeout=5.0 + ) + + # Count different entity types + selects = [] + text_sensors = [] + text_inputs = [] + numbers = [] + other_entities = [] + + for entity in entity_info: + if isinstance(entity, SelectInfo): + selects.append(entity) + elif isinstance(entity, TextSensorInfo): + text_sensors.append(entity) + elif isinstance(entity, TextInfo): + text_inputs.append(entity) + elif isinstance(entity, NumberInfo): + numbers.append(entity) + else: + other_entities.append(entity) + + # Verify we have our test entities - exact counts + assert len(selects) == 3, ( + f"Expected exactly 3 select entities, got {len(selects)}" + ) + assert len(text_sensors) == 3, ( + f"Expected exactly 3 text sensor entities, got {len(text_sensors)}" + ) + assert len(text_inputs) == 1, ( + f"Expected exactly 1 text input entity, got {len(text_inputs)}" + ) + + # Collect all select entity object_ids for error messages + select_ids = [s.object_id for s in selects] + + # Find our specific test entities + small_select = None + medium_select = None + large_select = None + + for select in selects: + if select.object_id == "small_select": + small_select = select + elif select.object_id == "medium_select": + medium_select = select + elif ( + select.object_id + == "large_select_with_many_options_to_create_larger_payload" + ): + large_select = select + + assert small_select is not None, ( + f"Could not find small_select entity. Found: {select_ids}" + ) + assert medium_select is not None, ( + f"Could not find medium_select entity. Found: {select_ids}" + ) + assert large_select is not None, ( + f"Could not find large_select entity. Found: {select_ids}" + ) + + # Verify the selects have the expected number of options + assert len(small_select.options) == 2, ( + f"Expected 2 options for small_select, got {len(small_select.options)}" + ) + assert len(medium_select.options) == 20, ( + f"Expected 20 options for medium_select, got {len(medium_select.options)}" + ) + assert len(large_select.options) == 50, ( + f"Expected 50 options for large_select, got {len(large_select.options)}" + ) + + # Collect all text sensor object_ids for error messages + text_sensor_ids = [t.object_id for t in text_sensors] + + # Verify text sensors with different value lengths + short_text_sensor = None + medium_text_sensor = None + long_text_sensor = None + + for text_sensor in text_sensors: + if text_sensor.object_id == "short_text_sensor": + short_text_sensor = text_sensor + elif text_sensor.object_id == "medium_text_sensor": + medium_text_sensor = text_sensor + elif text_sensor.object_id == "long_text_sensor_with_very_long_value": + long_text_sensor = text_sensor + + assert short_text_sensor is not None, ( + f"Could not find short_text_sensor. Found: {text_sensor_ids}" + ) + assert medium_text_sensor is not None, ( + f"Could not find medium_text_sensor. Found: {text_sensor_ids}" + ) + assert long_text_sensor is not None, ( + f"Could not find long_text_sensor. Found: {text_sensor_ids}" + ) + + # Check text input which can have a long max_length + text_input = None + text_input_ids = [t.object_id for t in text_inputs] + + for ti in text_inputs: + if ti.object_id == "test_text_input": + text_input = ti + break + + assert text_input is not None, ( + f"Could not find test_text_input. Found: {text_input_ids}" + ) + assert text_input.max_length == 255, ( + f"Expected max_length 255, got {text_input.max_length}" + ) + + # Verify total entity count - messages of various sizes were batched successfully + # We have: 3 selects + 3 text sensors + 1 text input + 1 number = 8 total + total_entities = len(entity_info) + assert total_entities == 8, f"Expected exactly 8 entities, got {total_entities}" + + # Check we have the expected entity types + assert len(numbers) == 1, ( + f"Expected exactly 1 number entity, got {len(numbers)}" + ) + assert len(other_entities) == 0, ( + f"Unexpected entity types found: {[type(e).__name__ for e in other_entities]}" + ) + + # Subscribe to state changes to verify batching works + # Collect keys from entity info to know what states to expect + expected_keys = {entity.key for entity in entity_info} + assert len(expected_keys) == 8, ( + f"Expected 8 unique entity keys, got {len(expected_keys)}" + ) + + received_keys: set[int] = set() + states_future: asyncio.Future[None] = loop.create_future() + + def on_state(state: EntityState) -> None: + """Track when states are received.""" + received_keys.add(state.key) + # Check if we've received states from all expected entities + if expected_keys.issubset(received_keys) and not states_future.done(): + states_future.set_result(None) + + client.subscribe_states(on_state) + + # Wait for states with timeout + try: + await asyncio.wait_for(states_future, timeout=5.0) + except asyncio.TimeoutError: + missing_keys = expected_keys - received_keys + pytest.fail( + f"Did not receive states from all entities within 5 seconds. " + f"Missing keys: {missing_keys}, " + f"Received {len(received_keys)} of {len(expected_keys)} expected states" + ) + + # Verify we received states from all entities + assert expected_keys.issubset(received_keys) + + # Check that various message sizes were handled correctly + # Small messages (4-byte header): type < 128, payload < 128 + # Medium messages (5-byte header): type < 128, payload 128-16383 OR type 128+, payload < 128 + # Large messages (6-byte header): type 128+, payload 128-16383 diff --git a/tests/integration/test_host_mode_basic.py b/tests/integration/test_host_mode_basic.py new file mode 100644 index 0000000000..fd52979784 --- /dev/null +++ b/tests/integration/test_host_mode_basic.py @@ -0,0 +1,22 @@ +"""Basic integration test for Host mode.""" + +from __future__ import annotations + +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_basic( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test basic Host mode functionality with API connection.""" + # Write, compile and run the ESPHome device, then connect to API + async with run_compiled(yaml_config), api_client_connected() as client: + # Verify we can get device info + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "host-test" diff --git a/tests/integration/test_host_mode_batch_delay.py b/tests/integration/test_host_mode_batch_delay.py new file mode 100644 index 0000000000..5165b90e47 --- /dev/null +++ b/tests/integration/test_host_mode_batch_delay.py @@ -0,0 +1,80 @@ +"""Integration test for API batch_delay setting.""" + +from __future__ import annotations + +import asyncio +import time + +from aioesphomeapi import EntityState +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_batch_delay( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test API with batch_delay set to 0ms - messages should be sent immediately without batching.""" + # Write, compile and run the ESPHome device, then connect to API + loop = asyncio.get_running_loop() + async with run_compiled(yaml_config), api_client_connected() as client: + # Verify we can get device info + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "host-batch-delay-test" + + # Subscribe to state changes + states: dict[int, EntityState] = {} + state_timestamps: dict[int, float] = {} + entity_count_future: asyncio.Future[int] = loop.create_future() + + def on_state(state: EntityState) -> None: + """Track when states are received.""" + states[state.key] = state + state_timestamps[state.key] = time.monotonic() + # When we have received all expected entities, resolve the future + if len(states) >= 7 and not entity_count_future.done(): + entity_count_future.set_result(len(states)) + + client.subscribe_states(on_state) + + # Wait for states from all entities with timeout + try: + entity_count = await asyncio.wait_for(entity_count_future, timeout=5.0) + except asyncio.TimeoutError: + pytest.fail( + f"Did not receive states from at least 7 entities within 5 seconds. " + f"Received {len(states)} states" + ) + + # Verify we received all states + assert entity_count >= 7, f"Expected at least 7 entities, got {entity_count}" + assert len(states) >= 7 # 3 sensors + 2 binary sensors + 2 switches + + # When batch_delay is 0ms, states are sent immediately without batching + # This means each state arrives in its own packet, which may actually be slower + # than batching due to network overhead + if state_timestamps: + first_timestamp = min(state_timestamps.values()) + last_timestamp = max(state_timestamps.values()) + time_spread = last_timestamp - first_timestamp + + # With batch_delay=0ms, states arrive individually which may take longer + # We just verify they all arrive within a reasonable time + assert time_spread < 1.0, f"States took {time_spread:.3f}s to arrive" + + # Also test list_entities - with batch_delay=0ms each entity is sent separately + start_time = time.monotonic() + entity_info, services = await client.list_entities_services() + end_time = time.monotonic() + + list_time = end_time - start_time + + # Verify we got all expected entities + assert len(entity_info) >= 7 # 3 sensors + 2 binary sensors + 2 switches + + # With batch_delay=0ms, listing sends each entity separately which may be slower + assert list_time < 1.0, f"list_entities took {list_time:.3f}s" diff --git a/tests/integration/test_host_mode_empty_string_options.py b/tests/integration/test_host_mode_empty_string_options.py new file mode 100644 index 0000000000..d2df839a75 --- /dev/null +++ b/tests/integration/test_host_mode_empty_string_options.py @@ -0,0 +1,110 @@ +"""Integration test for protobuf encoding of empty string options in select entities.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import EntityState, SelectInfo +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_empty_string_options( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that select entities with empty string options are correctly encoded in protobuf messages. + + This tests the fix for the bug where the force parameter was not passed in encode_string, + causing empty strings in repeated fields to be skipped during encoding but included in + size calculation, leading to protobuf decoding errors. + """ + # Write, compile and run the ESPHome device, then connect to API + loop = asyncio.get_running_loop() + async with run_compiled(yaml_config), api_client_connected() as client: + # Verify we can get device info + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "host-empty-string-test" + + # Get list of entities - this will encode ListEntitiesSelectResponse messages + # with empty string options that would trigger the bug + entity_info, services = await client.list_entities_services() + + # Find our select entities + select_entities = [e for e in entity_info if isinstance(e, SelectInfo)] + assert len(select_entities) == 3, ( + f"Expected 3 select entities, got {len(select_entities)}" + ) + + # Verify each select entity by name and check their options + selects_by_name = {e.name: e for e in select_entities} + + # Check "Select Empty First" - empty string at beginning + assert "Select Empty First" in selects_by_name + empty_first = selects_by_name["Select Empty First"] + assert len(empty_first.options) == 4 + assert empty_first.options[0] == "" # Empty string at beginning + assert empty_first.options[1] == "Option A" + assert empty_first.options[2] == "Option B" + assert empty_first.options[3] == "Option C" + + # Check "Select Empty Middle" - empty string in middle + assert "Select Empty Middle" in selects_by_name + empty_middle = selects_by_name["Select Empty Middle"] + assert len(empty_middle.options) == 5 + assert empty_middle.options[0] == "Option 1" + assert empty_middle.options[1] == "Option 2" + assert empty_middle.options[2] == "" # Empty string in middle + assert empty_middle.options[3] == "Option 3" + assert empty_middle.options[4] == "Option 4" + + # Check "Select Empty Last" - empty string at end + assert "Select Empty Last" in selects_by_name + empty_last = selects_by_name["Select Empty Last"] + assert len(empty_last.options) == 4 + assert empty_last.options[0] == "Choice X" + assert empty_last.options[1] == "Choice Y" + assert empty_last.options[2] == "Choice Z" + assert empty_last.options[3] == "" # Empty string at end + + # If we got here without protobuf decoding errors, the fix is working + # The bug would have caused "Invalid protobuf message" errors with trailing bytes + + # Also verify we can interact with the select entities + # Subscribe to state changes + states: dict[int, EntityState] = {} + state_change_future: asyncio.Future[None] = loop.create_future() + + def on_state(state: EntityState) -> None: + """Track state changes.""" + states[state.key] = state + # When we receive the state change for our select, resolve the future + if state.key == empty_first.key and not state_change_future.done(): + state_change_future.set_result(None) + + client.subscribe_states(on_state) + + # Try setting a select to an empty string option + # This further tests that empty strings are handled correctly + client.select_command(empty_first.key, "") + + # Wait for state update with timeout + try: + await asyncio.wait_for(state_change_future, timeout=5.0) + except asyncio.TimeoutError: + pytest.fail( + "Did not receive state update after setting select to empty string" + ) + + # Verify the state was set to empty string + assert empty_first.key in states + select_state = states[empty_first.key] + assert hasattr(select_state, "state") + assert select_state.state == "" + + # The test passes if no protobuf decoding errors occurred + # With the bug, we would have gotten "Invalid protobuf message" errors diff --git a/tests/integration/test_host_mode_entity_fields.py b/tests/integration/test_host_mode_entity_fields.py new file mode 100644 index 0000000000..cf3fa6916a --- /dev/null +++ b/tests/integration/test_host_mode_entity_fields.py @@ -0,0 +1,93 @@ +"""Integration test for entity bit-packed fields.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import EntityCategory, EntityState +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_entity_fields( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test entity bit-packed fields work correctly with all possible values.""" + # Write, compile and run the ESPHome device, then connect to API + async with run_compiled(yaml_config), api_client_connected() as client: + # Get all entities + entities = await client.list_entities_services() + + # Create a map of entity names to entity info + entity_map = {} + for entity in entities[0]: + if hasattr(entity, "name"): + entity_map[entity.name] = entity + + # Test entities that should be visible via API (non-internal) + visible_test_cases = [ + # (entity_name, expected_disabled_by_default, expected_entity_category) + ("Test Normal Sensor", False, EntityCategory.NONE), + ("Test Disabled Sensor", True, EntityCategory.NONE), + ("Test Diagnostic Sensor", False, EntityCategory.DIAGNOSTIC), + ("Test Switch", True, EntityCategory.CONFIG), + ("Test Binary Sensor", False, EntityCategory.CONFIG), + ("Test Number", False, EntityCategory.DIAGNOSTIC), + ] + + # Test entities that should NOT be visible via API (internal) + internal_entities = [ + "Test Internal Sensor", + "Test Mixed Flags Sensor", + "Test All Flags Sensor", + "Test Select", + ] + + # Verify visible entities + for entity_name, expected_disabled, expected_category in visible_test_cases: + assert entity_name in entity_map, ( + f"Entity '{entity_name}' not found - it should be visible via API" + ) + entity = entity_map[entity_name] + + # Check disabled_by_default flag + assert entity.disabled_by_default == expected_disabled, ( + f"{entity_name}: disabled_by_default flag mismatch - " + f"expected {expected_disabled}, got {entity.disabled_by_default}" + ) + + # Check entity_category + assert entity.entity_category == expected_category, ( + f"{entity_name}: entity_category mismatch - " + f"expected {expected_category}, got {entity.entity_category}" + ) + + # Verify internal entities are NOT visible + for entity_name in internal_entities: + assert entity_name not in entity_map, ( + f"Entity '{entity_name}' found in API response - " + f"internal entities should not be exposed via API" + ) + + # Subscribe to states to verify has_state flag works + states: dict[int, EntityState] = {} + state_received = asyncio.Event() + + def on_state(state: EntityState) -> None: + states[state.key] = state + state_received.set() + + client.subscribe_states(on_state) + + # Wait for at least one state + try: + await asyncio.wait_for(state_received.wait(), timeout=5.0) + except asyncio.TimeoutError: + pytest.fail("No states received within 5 seconds") + + # Verify we received states (which means has_state flag is working) + assert len(states) > 0, "No states received - has_state flag may not be working" diff --git a/tests/integration/test_host_mode_fan_preset.py b/tests/integration/test_host_mode_fan_preset.py new file mode 100644 index 0000000000..1d956a7290 --- /dev/null +++ b/tests/integration/test_host_mode_fan_preset.py @@ -0,0 +1,152 @@ +"""Integration test for fan preset mode behavior.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import FanInfo, FanState +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_fan_preset( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test fan preset mode behavior according to Home Assistant guidelines.""" + # Write, compile and run the ESPHome device, then connect to API + async with run_compiled(yaml_config), api_client_connected() as client: + # Get all fan entities + entities = await client.list_entities_services() + fans: list[FanInfo] = [] + for entity_list in entities: + for entity in entity_list: + if isinstance(entity, FanInfo): + fans.append(entity) + + # Create a map of fan names to entity info + fan_map = {fan.name: fan for fan in fans} + + # Verify we have our test fans + assert "Test Fan with Presets" in fan_map + assert "Test Fan Simple" in fan_map + assert "Test Fan No Speed" in fan_map + + # Get fan with presets + fan_presets = fan_map["Test Fan with Presets"] + assert fan_presets.supports_speed is True + assert fan_presets.supported_speed_count == 5 + assert fan_presets.supports_oscillation is True + assert fan_presets.supports_direction is True + assert set(fan_presets.supported_preset_modes) == {"Eco", "Sleep", "Turbo"} + + # Subscribe to states + states: dict[int, FanState] = {} + state_event = asyncio.Event() + + def on_state(state: FanState) -> None: + if isinstance(state, FanState): + states[state.key] = state + state_event.set() + + client.subscribe_states(on_state) + + # Test 1: Turn on fan without speed or preset - should set speed to 100% + state_event.clear() + client.fan_command( + key=fan_presets.key, + state=True, + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + fan_state = states[fan_presets.key] + assert fan_state.state is True + assert fan_state.speed_level == 5 # Should be max speed (100%) + assert fan_state.preset_mode == "" + + # Turn off + state_event.clear() + client.fan_command( + key=fan_presets.key, + state=False, + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + # Test 2: Turn on fan with preset mode - should NOT set speed to 100% + state_event.clear() + client.fan_command( + key=fan_presets.key, + state=True, + preset_mode="Eco", + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + fan_state = states[fan_presets.key] + assert fan_state.state is True + assert fan_state.preset_mode == "Eco" + # Speed should be whatever the preset sets, not forced to 100% + + # Test 3: Setting speed should clear preset mode + state_event.clear() + client.fan_command( + key=fan_presets.key, + speed_level=3, + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + fan_state = states[fan_presets.key] + assert fan_state.state is True + assert fan_state.speed_level == 3 + assert fan_state.preset_mode == "" # Preset mode should be cleared + + # Test 4: Setting preset mode should work when fan is already on + state_event.clear() + client.fan_command( + key=fan_presets.key, + preset_mode="Sleep", + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + fan_state = states[fan_presets.key] + assert fan_state.state is True + assert fan_state.preset_mode == "Sleep" + + # Turn off + state_event.clear() + client.fan_command( + key=fan_presets.key, + state=False, + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + # Test 5: Turn on fan with specific speed + state_event.clear() + client.fan_command( + key=fan_presets.key, + state=True, + speed_level=2, + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + fan_state = states[fan_presets.key] + assert fan_state.state is True + assert fan_state.speed_level == 2 + assert fan_state.preset_mode == "" + + # Test 6: Test fan with no speed support + fan_no_speed = fan_map["Test Fan No Speed"] + assert fan_no_speed.supports_speed is False + + state_event.clear() + client.fan_command( + key=fan_no_speed.key, + state=True, + ) + await asyncio.wait_for(state_event.wait(), timeout=2.0) + + fan_state = states[fan_no_speed.key] + assert fan_state.state is True + # No speed should be set for fans that don't support speed diff --git a/tests/integration/test_host_mode_many_entities.py b/tests/integration/test_host_mode_many_entities.py new file mode 100644 index 0000000000..d5622e6fa4 --- /dev/null +++ b/tests/integration/test_host_mode_many_entities.py @@ -0,0 +1,57 @@ +"""Integration test for many entities to test API batching.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import EntityState +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_many_entities( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test API batching with many entities of different types.""" + # Write, compile and run the ESPHome device, then connect to API + loop = asyncio.get_running_loop() + async with run_compiled(yaml_config), api_client_connected() as client: + # Subscribe to state changes + states: dict[int, EntityState] = {} + entity_count_future: asyncio.Future[int] = loop.create_future() + + def on_state(state: EntityState) -> None: + states[state.key] = state + # When we have received states from a good number of entities, resolve the future + if len(states) >= 50 and not entity_count_future.done(): + entity_count_future.set_result(len(states)) + + client.subscribe_states(on_state) + + # Wait for states from at least 50 entities with timeout + try: + entity_count = await asyncio.wait_for(entity_count_future, timeout=10.0) + except asyncio.TimeoutError: + pytest.fail( + f"Did not receive states from at least 50 entities within 10 seconds. " + f"Received {len(states)} states: {list(states.keys())}" + ) + + # Verify we received a good number of entity states + assert entity_count >= 50, f"Expected at least 50 entities, got {entity_count}" + assert len(states) >= 50, f"Expected at least 50 states, got {len(states)}" + + # Verify we have different entity types by checking some expected values + sensor_states = [ + s + for s in states.values() + if hasattr(s, "state") and isinstance(s.state, float) + ] + + assert len(sensor_states) >= 50, ( + f"Expected at least 50 sensor states, got {len(sensor_states)}" + ) diff --git a/tests/integration/test_host_mode_many_entities_multiple_connections.py b/tests/integration/test_host_mode_many_entities_multiple_connections.py new file mode 100644 index 0000000000..a4e5f8a45c --- /dev/null +++ b/tests/integration/test_host_mode_many_entities_multiple_connections.py @@ -0,0 +1,71 @@ +"""Integration test for shared buffer optimization with multiple API connections.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import EntityState +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_many_entities_multiple_connections( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test shared buffer optimization with multiple API connections.""" + # Write, compile and run the ESPHome device + loop = asyncio.get_running_loop() + async with ( + run_compiled(yaml_config), + api_client_connected() as client1, + api_client_connected() as client2, + ): + # Subscribe both clients to state changes + states1: dict[int, EntityState] = {} + states2: dict[int, EntityState] = {} + + client1_ready = loop.create_future() + client2_ready = loop.create_future() + + def on_state1(state: EntityState) -> None: + states1[state.key] = state + if len(states1) >= 20 and not client1_ready.done(): + client1_ready.set_result(len(states1)) + + def on_state2(state: EntityState) -> None: + states2[state.key] = state + if len(states2) >= 20 and not client2_ready.done(): + client2_ready.set_result(len(states2)) + + client1.subscribe_states(on_state1) + client2.subscribe_states(on_state2) + + # Wait for both clients to receive states + try: + count1, count2 = await asyncio.gather( + asyncio.wait_for(client1_ready, timeout=10.0), + asyncio.wait_for(client2_ready, timeout=10.0), + ) + except asyncio.TimeoutError: + pytest.fail( + f"One or both clients did not receive enough states within 10 seconds. " + f"Client1: {len(states1)}, Client2: {len(states2)}" + ) + + # Verify both clients received states successfully + assert count1 >= 20, ( + f"Client 1 should have received at least 20 states, got {count1}" + ) + assert count2 >= 20, ( + f"Client 2 should have received at least 20 states, got {count2}" + ) + + # Verify both clients received the same entity keys (same device state) + common_keys = set(states1.keys()) & set(states2.keys()) + assert len(common_keys) >= 20, ( + f"Expected at least 20 common entity keys, got {len(common_keys)}" + ) diff --git a/tests/integration/test_host_mode_noise_encryption.py b/tests/integration/test_host_mode_noise_encryption.py new file mode 100644 index 0000000000..53873f2760 --- /dev/null +++ b/tests/integration/test_host_mode_noise_encryption.py @@ -0,0 +1,53 @@ +"""Integration test for Host mode with noise encryption.""" + +from __future__ import annotations + +from aioesphomeapi import InvalidEncryptionKeyAPIError +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + +# The API key for noise encryption +NOISE_KEY = "N4Yle5YirwZhPiHHsdZLdOA73ndj/84veVaLhTvxCuU=" + + +@pytest.mark.asyncio +async def test_host_mode_noise_encryption( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test Host mode with noise encryption enabled.""" + # Write, compile and run the ESPHome device, then connect to API + # The API client should handle noise encryption automatically + async with ( + run_compiled(yaml_config), + api_client_connected(noise_psk=NOISE_KEY) as client, + ): + # If we can get device info, the encryption is working + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "host-noise-test" + + # List entities to ensure the encrypted connection is fully functional + entities = await client.list_entities_services() + assert entities is not None + + +@pytest.mark.asyncio +async def test_host_mode_noise_encryption_wrong_key( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that connection fails with wrong encryption key.""" + # Write, compile and run the ESPHome device + async with run_compiled(yaml_config): + # Try to connect with wrong key - should fail with InvalidEncryptionKeyAPIError + with pytest.raises(InvalidEncryptionKeyAPIError): + async with api_client_connected( + noise_psk="wrong_key_that_should_not_work", + timeout=5, # Shorter timeout for expected failure + ) as client: + # This should not be reached + await client.device_info() diff --git a/tests/integration/test_host_mode_reconnect.py b/tests/integration/test_host_mode_reconnect.py new file mode 100644 index 0000000000..8f69193559 --- /dev/null +++ b/tests/integration/test_host_mode_reconnect.py @@ -0,0 +1,28 @@ +"""Integration test for Host mode reconnection.""" + +from __future__ import annotations + +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_reconnect( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test reconnecting to a Host mode device.""" + # Write, compile and run the ESPHome device + async with run_compiled(yaml_config): + # First connection + async with api_client_connected() as client: + device_info = await client.device_info() + assert device_info is not None + + # Reconnect with a new client + async with api_client_connected() as client2: + device_info2 = await client2.device_info() + assert device_info2 is not None + assert device_info2.name == device_info.name diff --git a/tests/integration/test_host_mode_sensor.py b/tests/integration/test_host_mode_sensor.py new file mode 100644 index 0000000000..f0c938da1c --- /dev/null +++ b/tests/integration/test_host_mode_sensor.py @@ -0,0 +1,49 @@ +"""Integration test for Host mode with sensor.""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import EntityState +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_host_mode_with_sensor( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test Host mode with a sensor component.""" + # Write, compile and run the ESPHome device, then connect to API + async with run_compiled(yaml_config), api_client_connected() as client: + # Subscribe to state changes + states: dict[int, EntityState] = {} + sensor_future: asyncio.Future[EntityState] = asyncio.Future() + + def on_state(state: EntityState) -> None: + states[state.key] = state + # If this is our sensor with value 42.0, resolve the future + if ( + hasattr(state, "state") + and state.state == 42.0 + and not sensor_future.done() + ): + sensor_future.set_result(state) + + client.subscribe_states(on_state) + + # Wait for sensor with specific value (42.0) with timeout + try: + test_sensor_state = await asyncio.wait_for(sensor_future, timeout=5.0) + except asyncio.TimeoutError: + pytest.fail( + f"Sensor with value 42.0 not received within 5 seconds. " + f"Received states: {list(states.values())}" + ) + + # Verify the sensor state + assert test_sensor_state.state == 42.0 + assert len(states) > 0, "No states received" diff --git a/tests/integration/test_large_message_batching.py b/tests/integration/test_large_message_batching.py new file mode 100644 index 0000000000..399fd39dd3 --- /dev/null +++ b/tests/integration/test_large_message_batching.py @@ -0,0 +1,59 @@ +"""Integration test for API handling of large messages exceeding batch size.""" + +from __future__ import annotations + +from aioesphomeapi import SelectInfo +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_large_message_batching( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test API can handle large messages (>1390 bytes) in batches.""" + # Write, compile and run the ESPHome device, then connect to API + async with run_compiled(yaml_config), api_client_connected() as client: + # Verify we can get device info + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "large-message-test" + + # List entities - this will include our select with many options + entity_info, services = await client.list_entities_services() + + # Find our large select entity + large_select = None + for entity in entity_info: + if isinstance(entity, SelectInfo) and entity.object_id == "large_select": + large_select = entity + break + + assert large_select is not None, "Could not find large_select entity" + + # Verify the select has all its options + # We created 100 options with long names + assert len(large_select.options) == 100, ( + f"Expected 100 options, got {len(large_select.options)}" + ) + + # Verify all options are present and correct + for i in range(100): + expected_option = f"Option {i:03d} - This is a very long option name to make the message larger than the typical batch size of 1390 bytes" + assert expected_option in large_select.options, ( + f"Missing option: {expected_option}" + ) + + # Also verify we can still receive other entities in the same batch + # Count total entities - should have at least our select plus some sensors + entity_count = len(entity_info) + assert entity_count >= 4, f"Expected at least 4 entities, got {entity_count}" + + # Verify we have different entity types (not just selects) + entity_types = {type(entity).__name__ for entity in entity_info} + assert len(entity_types) >= 2, ( + f"Expected multiple entity types, got {entity_types}" + ) diff --git a/tests/integration/types.py b/tests/integration/types.py new file mode 100644 index 0000000000..6fc3e9435e --- /dev/null +++ b/tests/integration/types.py @@ -0,0 +1,44 @@ +"""Type definitions for integration tests.""" + +from __future__ import annotations + +import asyncio +from collections.abc import Awaitable, Callable +from contextlib import AbstractAsyncContextManager +from pathlib import Path +from typing import Protocol + +from aioesphomeapi import APIClient + +ConfigWriter = Callable[[str, str | None], Awaitable[Path]] +CompileFunction = Callable[[Path], Awaitable[Path]] +RunFunction = Callable[[Path], Awaitable[asyncio.subprocess.Process]] +RunCompiledFunction = Callable[[str, str | None], AbstractAsyncContextManager[None]] +WaitFunction = Callable[[APIClient, float], Awaitable[bool]] + + +class APIClientFactory(Protocol): + """Protocol for API client factory.""" + + def __call__( # noqa: E704 + self, + address: str = "localhost", + port: int | None = None, + password: str = "", + noise_psk: str | None = None, + client_info: str = "integration-test", + ) -> AbstractAsyncContextManager[APIClient]: ... + + +class APIClientConnectedFactory(Protocol): + """Protocol for connected API client factory.""" + + def __call__( # noqa: E704 + self, + address: str = "localhost", + port: int | None = None, + password: str = "", + noise_psk: str | None = None, + client_info: str = "integration-test", + timeout: float = 30, + ) -> AbstractAsyncContextManager[APIClient]: ... diff --git a/tests/test_build_components/build_components_base.esp32-c6-idf.yaml b/tests/test_build_components/build_components_base.esp32-c6-idf.yaml new file mode 100644 index 0000000000..9dbc465ca2 --- /dev/null +++ b/tests/test_build_components/build_components_base.esp32-c6-idf.yaml @@ -0,0 +1,17 @@ +esphome: + name: componenttestesp32c6idf + friendly_name: $component_name + +esp32: + board: esp32-c6-devkitc-1 + framework: + type: esp-idf + +logger: + level: VERY_VERBOSE + +packages: + component_under_test: !include + file: $component_test_file + vars: + component_test_file: $component_test_file diff --git a/tests/test_build_components/build_components_base.esp32-p4-idf.yaml b/tests/test_build_components/build_components_base.esp32-p4-idf.yaml new file mode 100644 index 0000000000..e2b975f643 --- /dev/null +++ b/tests/test_build_components/build_components_base.esp32-p4-idf.yaml @@ -0,0 +1,18 @@ +esphome: + name: componenttestesp32p4idf + friendly_name: $component_name + +esp32: + board: esp32-p4-evboard + framework: + type: esp-idf + +logger: + level: VERY_VERBOSE + +packages: + component_under_test: !include + file: $component_test_file + vars: + component_test_file: $component_test_file +