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..5c8229ba12 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.0b1 # 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..c5c63b8dfc 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -432,7 +432,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; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index b4646a2d7d..93ba9248b4 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,84 @@ 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 size = 0; + msg.calculate_size(size); + + // Calculate total size with padding for buffer allocation + uint16_t total_size = + static_cast(size) + conn->helper_->frame_header_padding() + conn->helper_->frame_footer_size(); + + // Check if it fits + if (total_size > remaining_size) { + return 0; // Doesn't fit + } + + // Allocate exact buffer space needed (just the payload, not the overhead) + ProtoWriteBuffer buffer = + is_single ? conn->allocate_single_message_buffer(size) : conn->allocate_batch_message_buffer(size); + + // Encode directly into buffer + msg.encode(buffer); + return 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(); + resp.key = binary_sensor->get_object_id_hash(); + 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 = @@ -323,9 +320,11 @@ bool APIConnection::try_send_cover_state_(cover::Cover *cover) { 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); + 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 +333,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 +367,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; @@ -389,9 +388,11 @@ bool APIConnection::try_send_fan_state_(fan::Fan *fan) { 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); + 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 +402,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 +429,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; @@ -454,9 +455,11 @@ bool APIConnection::try_send_light_state_(light::LightState *light) { if (light->supports_effects()) resp.effect = light->get_effect_name(); resp.key = light->get_object_id_hash(); - return this->send_light_state_response(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 +482,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 +523,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(); +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(); resp.key = sensor->get_object_id_hash(); - return this->send_sensor_state_response(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 +552,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; +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; resp.key = a_switch->get_object_id_hash(); - return this->send_switch_state_response(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,46 +598,44 @@ 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(); +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(); resp.key = text_sensor->get_object_id_hash(); - return this->send_text_sensor_state_response(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(); auto traits = climate->get_traits(); @@ -663,9 +664,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 +699,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 +733,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(); +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(); resp.key = number->get_object_id_hash(); - return this->send_number_state_response(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 +761,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 +777,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); + 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 +814,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); + 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 +851,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); + 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 +889,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(); +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(); resp.key = text->get_object_id_hash(); - return this->send_text_state_response(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 +930,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(); +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(); resp.key = select->get_object_id_hash(); - return this->send_select_state_response(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 +970,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 +991,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); +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); resp.key = a_lock->get_object_id_hash(); - return this->send_lock_state_response(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 +1039,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); + 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 +1063,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 +1082,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 +1095,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); + 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 +1118,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 +1154,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 +1205,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 +1351,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); + 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 +1413,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.event_type = event_type; resp.key = event->get_object_id_hash(); - return this->send_event_response(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 +1461,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); + 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 +1489,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); @@ -1530,7 +1551,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 +1622,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,7 +1668,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) { } return false; } -bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) { +bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) { if (!this->try_to_clear_buffer(message_type != 29)) { // SubscribeLogsResponse return false; } @@ -1670,17 +1691,350 @@ 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; + uint32_t remaining_size = MAX_PACKET_SIZE; + + // 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 + 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; + items_processed++; + } + + 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..34c7dcd880 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,67 @@ 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()); + shared_buf.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.insert(shared_buf.begin(), header_padding, 0); + 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(); + size_t current_size = shared_buf.size(); + + if (is_first_message) { + // For first message, initialize buffer with header padding + uint8_t header_padding = this->helper_->frame_header_padding(); + shared_buf.clear(); + shared_buf.reserve(message_size + header_padding); + shared_buf.resize(header_padding); + // Fill header padding with zeros + std::fill(shared_buf.begin(), shared_buf.end(), 0); + } else { + // For subsequent messages, add footer space for previous message and header for this message + uint8_t footer_size = this->helper_->frame_footer_size(); + uint8_t header_padding = this->helper_->frame_header_padding(); + + // Reserve additional space for everything + shared_buf.reserve(current_size + footer_size + header_padding + message_size); + + // Single resize to add both footer and header padding + size_t new_size = current_size + footer_size + header_padding; + shared_buf.resize(new_size); + + // Fill the newly added bytes with zeros (footer + header padding) + std::fill(shared_buf.begin() + current_size, shared_buf.end(), 0); + } + + 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 fields + template static void fill_entity_info_base(esphome::EntityBase *entity, ResponseT &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 +301,137 @@ 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); + // 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 +441,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 +461,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_pb2.cpp b/esphome/components/api/api_pb2.cpp index e3181b6166..2d609f6dd6 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: diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index c0927ebdc0..8b3f7a7b2a 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, @@ -254,6 +255,11 @@ enum UpdateCommand : uint32_t { 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 +275,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 +296,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 +313,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 +330,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 +345,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 +360,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 +375,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 +390,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 +405,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 +441,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 +456,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 +471,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 @@ -420,6 +486,11 @@ class SubscribeStatesRequest : public ProtoMessage { }; class ListEntitiesBinarySensorResponse : public ProtoMessage { public: + 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 object_id{}; uint32_t key{0}; std::string name{}; @@ -442,6 +513,11 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage { }; class BinarySensorStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; bool state{false}; bool missing_state{false}; @@ -457,6 +533,11 @@ class BinarySensorStateResponse : public ProtoMessage { }; class ListEntitiesCoverResponse : public ProtoMessage { public: + 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 std::string object_id{}; uint32_t key{0}; std::string name{}; @@ -482,6 +563,11 @@ class ListEntitiesCoverResponse : public ProtoMessage { }; class CoverStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; enums::LegacyCoverState legacy_state{}; float position{0.0f}; @@ -499,6 +585,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{}; @@ -519,6 +610,11 @@ class CoverCommandRequest : public ProtoMessage { }; class ListEntitiesFanResponse : public ProtoMessage { public: + 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 std::string object_id{}; uint32_t key{0}; std::string name{}; @@ -544,6 +640,11 @@ class ListEntitiesFanResponse : public ProtoMessage { }; class FanStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; bool state{false}; bool oscillating{false}; @@ -564,6 +665,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}; @@ -590,6 +696,11 @@ class FanCommandRequest : public ProtoMessage { }; class ListEntitiesLightResponse : public ProtoMessage { public: + 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::string object_id{}; uint32_t key{0}; std::string name{}; @@ -618,6 +729,11 @@ class ListEntitiesLightResponse : public ProtoMessage { }; class LightStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; bool state{false}; float brightness{0.0f}; @@ -644,6 +760,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}; @@ -684,6 +805,11 @@ class LightCommandRequest : public ProtoMessage { }; class ListEntitiesSensorResponse : public ProtoMessage { public: + 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 object_id{}; uint32_t key{0}; std::string name{}; @@ -710,6 +836,11 @@ class ListEntitiesSensorResponse : public ProtoMessage { }; class SensorStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; float state{0.0f}; bool missing_state{false}; @@ -725,6 +856,11 @@ class SensorStateResponse : public ProtoMessage { }; class ListEntitiesSwitchResponse : public ProtoMessage { public: + 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 std::string object_id{}; uint32_t key{0}; std::string name{}; @@ -747,6 +883,11 @@ class ListEntitiesSwitchResponse : public ProtoMessage { }; class SwitchStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; bool state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -761,6 +902,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; @@ -775,6 +921,11 @@ class SwitchCommandRequest : public ProtoMessage { }; class ListEntitiesTextSensorResponse : public ProtoMessage { public: + 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 object_id{}; uint32_t key{0}; std::string name{}; @@ -796,6 +947,11 @@ class ListEntitiesTextSensorResponse : public ProtoMessage { }; class TextSensorStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; std::string state{}; bool missing_state{false}; @@ -812,6 +968,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 +986,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 +1006,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 +1023,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 +1040,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 +1068,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 +1090,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 +1105,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 +1125,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 +1144,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 +1159,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 +1190,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 +1232,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; @@ -1030,6 +1251,11 @@ class ExecuteServiceRequest : public ProtoMessage { }; class ListEntitiesCameraResponse : public ProtoMessage { public: + 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 std::string object_id{}; uint32_t key{0}; std::string name{}; @@ -1050,6 +1276,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 +1297,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; @@ -1079,6 +1315,11 @@ class CameraImageRequest : public ProtoMessage { }; class ListEntitiesClimateResponse : public ProtoMessage { public: + 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 std::string object_id{}; uint32_t key{0}; std::string name{}; @@ -1117,6 +1358,11 @@ class ListEntitiesClimateResponse : public ProtoMessage { }; class ClimateStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; enums::ClimateMode mode{}; float current_temperature{0.0f}; @@ -1145,6 +1391,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{}; @@ -1181,6 +1432,11 @@ class ClimateCommandRequest : public ProtoMessage { }; class ListEntitiesNumberResponse : public ProtoMessage { public: + 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 std::string object_id{}; uint32_t key{0}; std::string name{}; @@ -1207,6 +1463,11 @@ class ListEntitiesNumberResponse : public ProtoMessage { }; class NumberStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; float state{0.0f}; bool missing_state{false}; @@ -1222,6 +1483,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; @@ -1235,6 +1501,11 @@ class NumberCommandRequest : public ProtoMessage { }; class ListEntitiesSelectResponse : public ProtoMessage { public: + 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::string object_id{}; uint32_t key{0}; std::string name{}; @@ -1256,6 +1527,11 @@ class ListEntitiesSelectResponse : public ProtoMessage { }; class SelectStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; std::string state{}; bool missing_state{false}; @@ -1272,6 +1548,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; @@ -1286,6 +1567,11 @@ class SelectCommandRequest : public ProtoMessage { }; class ListEntitiesSirenResponse : public ProtoMessage { public: + 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::string object_id{}; uint32_t key{0}; std::string name{}; @@ -1309,6 +1595,11 @@ class ListEntitiesSirenResponse : public ProtoMessage { }; class SirenStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; bool state{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -1323,6 +1614,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}; @@ -1345,6 +1641,11 @@ class SirenCommandRequest : public ProtoMessage { }; class ListEntitiesLockResponse : public ProtoMessage { public: + 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 std::string object_id{}; uint32_t key{0}; std::string name{}; @@ -1369,6 +1670,11 @@ class ListEntitiesLockResponse : public ProtoMessage { }; class LockStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; enums::LockState state{}; void encode(ProtoWriteBuffer buffer) const override; @@ -1383,6 +1689,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}; @@ -1400,6 +1711,11 @@ class LockCommandRequest : public ProtoMessage { }; class ListEntitiesButtonResponse : public ProtoMessage { public: + 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 object_id{}; uint32_t key{0}; std::string name{}; @@ -1421,6 +1737,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; @@ -1450,6 +1771,11 @@ class MediaPlayerSupportedFormat : public ProtoMessage { }; class ListEntitiesMediaPlayerResponse : public ProtoMessage { public: + 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 std::string object_id{}; uint32_t key{0}; std::string name{}; @@ -1472,6 +1798,11 @@ class ListEntitiesMediaPlayerResponse : public ProtoMessage { }; class MediaPlayerStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; enums::MediaPlayerState state{}; float volume{0.0f}; @@ -1488,6 +1819,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 +1846,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 +1878,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 +1918,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 +1935,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 +1955,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 +1975,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 +2036,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 +2055,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 +2072,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 +2090,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 +2110,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 +2131,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 +2149,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 +2169,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 +2188,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 +2208,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 +2223,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 +2242,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 +2261,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 +2279,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 +2297,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 +2316,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 +2335,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 +2350,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 +2369,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 +2387,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 +2404,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 +2437,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 +2459,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 +2490,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 +2509,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 +2528,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 +2551,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 +2572,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 +2603,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 +2618,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 +2638,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; @@ -2134,6 +2655,11 @@ class VoiceAssistantSetConfiguration : public ProtoMessage { }; class ListEntitiesAlarmControlPanelResponse : public ProtoMessage { public: + 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 std::string object_id{}; uint32_t key{0}; std::string name{}; @@ -2157,6 +2683,11 @@ class ListEntitiesAlarmControlPanelResponse : public ProtoMessage { }; class AlarmControlPanelStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; enums::AlarmControlPanelState state{}; void encode(ProtoWriteBuffer buffer) const override; @@ -2171,6 +2702,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{}; @@ -2187,6 +2723,11 @@ class AlarmControlPanelCommandRequest : public ProtoMessage { }; class ListEntitiesTextResponse : public ProtoMessage { public: + 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 std::string object_id{}; uint32_t key{0}; std::string name{}; @@ -2211,6 +2752,11 @@ class ListEntitiesTextResponse : public ProtoMessage { }; class TextStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; std::string state{}; bool missing_state{false}; @@ -2227,6 +2773,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; @@ -2241,6 +2792,11 @@ class TextCommandRequest : public ProtoMessage { }; class ListEntitiesDateResponse : public ProtoMessage { public: + 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 std::string object_id{}; uint32_t key{0}; std::string name{}; @@ -2261,6 +2817,11 @@ class ListEntitiesDateResponse : public ProtoMessage { }; class DateStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; bool missing_state{false}; uint32_t year{0}; @@ -2278,6 +2839,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}; @@ -2294,6 +2860,11 @@ class DateCommandRequest : public ProtoMessage { }; class ListEntitiesTimeResponse : public ProtoMessage { public: + 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 std::string object_id{}; uint32_t key{0}; std::string name{}; @@ -2314,6 +2885,11 @@ class ListEntitiesTimeResponse : public ProtoMessage { }; class TimeStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; bool missing_state{false}; uint32_t hour{0}; @@ -2331,6 +2907,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}; @@ -2347,6 +2928,11 @@ class TimeCommandRequest : public ProtoMessage { }; class ListEntitiesEventResponse : public ProtoMessage { public: + 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 object_id{}; uint32_t key{0}; std::string name{}; @@ -2369,6 +2955,11 @@ class ListEntitiesEventResponse : public ProtoMessage { }; class EventResponse : public ProtoMessage { public: + 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 uint32_t key{0}; std::string event_type{}; void encode(ProtoWriteBuffer buffer) const override; @@ -2383,6 +2974,11 @@ class EventResponse : public ProtoMessage { }; class ListEntitiesValveResponse : public ProtoMessage { public: + 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 object_id{}; uint32_t key{0}; std::string name{}; @@ -2407,6 +3003,11 @@ class ListEntitiesValveResponse : public ProtoMessage { }; class ValveStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; float position{0.0f}; enums::ValveOperation current_operation{}; @@ -2422,6 +3023,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}; @@ -2438,6 +3044,11 @@ class ValveCommandRequest : public ProtoMessage { }; class ListEntitiesDateTimeResponse : public ProtoMessage { public: + 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 std::string object_id{}; uint32_t key{0}; std::string name{}; @@ -2458,6 +3069,11 @@ class ListEntitiesDateTimeResponse : public ProtoMessage { }; class DateTimeStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; bool missing_state{false}; uint32_t epoch_seconds{0}; @@ -2473,6 +3089,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; @@ -2486,6 +3107,11 @@ class DateTimeCommandRequest : public ProtoMessage { }; class ListEntitiesUpdateResponse : public ProtoMessage { public: + 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 object_id{}; uint32_t key{0}; std::string name{}; @@ -2507,6 +3133,11 @@ class ListEntitiesUpdateResponse : public ProtoMessage { }; class UpdateStateResponse : public ProtoMessage { public: + 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 uint32_t key{0}; bool missing_state{false}; bool in_progress{false}; @@ -2530,6 +3161,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..4edcc90f4a 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__ @@ -48,7 +48,10 @@ async def async_run_logs(config: dict[str, Any], address: str) -> None: 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) 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..5265c4520d 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_; } @@ -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..7aeb818306 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) { @@ -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/bme280_base/bme280_base.cpp b/esphome/components/bme280_base/bme280_base.cpp index 76e20836c7..142a03fe1c 100644 --- a/esphome/components/bme280_base/bme280_base.cpp +++ b/esphome/components/bme280_base/bme280_base.cpp @@ -88,7 +88,7 @@ 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 @@ -182,7 +182,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 +207,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/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..b5d4c83f5e 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,28 @@ 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", +} def get_cpu_frequencies(*frequencies): @@ -88,8 +105,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. @@ -189,7 +208,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 +225,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 +315,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 +388,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 +467,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 +558,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 +574,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( @@ -638,7 +680,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 +711,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 +724,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 +768,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 +781,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..824c2b9dbc 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; } @@ -408,10 +408,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_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_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..61eed1c029 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,6 +260,9 @@ 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) diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 1a6071c9fe..6d60f1638c 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -172,7 +172,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 +219,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 +306,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(); }); @@ -731,11 +731,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_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index cfcf7869d4..a7551571dd 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->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..87bf4939a0 100644 --- a/esphome/components/fan/fan.cpp +++ b/esphome/components/fan/fan.cpp @@ -189,8 +189,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..2de3f1d9f8 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 I2S Audio 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..1ce98d51d3 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -40,7 +40,7 @@ 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_) { diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index d85409f1a8..f4c761ecc0 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(); 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..b3fbc31fe6 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,7 +14,7 @@ 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 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_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..59a3398ce8 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -221,12 +221,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_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..456ae25e65 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), 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..ab1e20859c 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)); } } @@ -61,8 +61,7 @@ void NextionBinarySensor::set_state(bool state, bool publish, bool send_to_nexti 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..fcd665917c 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,74 +285,74 @@ 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)); + ESP_LOGD(TAG, "TFT upload done: %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_); + 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(); } 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"); } return successful; } diff --git a/esphome/components/nextion/nextion_upload_idf.cpp b/esphome/components/nextion/nextion_upload_idf.cpp index 7541a57d56..8f54fbd8ac 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,23 +336,23 @@ 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)); + ESP_LOGD(TAG, "TFT upload done: %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_); + 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(); } 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"); } return successful; } diff --git a/esphome/components/nextion/sensor/nextion_sensor.cpp b/esphome/components/nextion/sensor/nextion_sensor.cpp index 6cc641fcf3..9be49e3476 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); } } @@ -93,7 +93,7 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) { } 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..a1d45f55e0 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()); } } @@ -42,7 +42,7 @@ void NextionTextSensor::set_state(const std::string &state, bool publish, bool s 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/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/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/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.h b/esphome/components/sensor/sensor.h index 98356c943d..ab9ff1565c 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -1,9 +1,9 @@ #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 @@ -13,13 +13,17 @@ 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()); \ } \ 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_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..6d38833ebd 100644 --- a/esphome/components/status_led/light/status_led_light.cpp +++ b/esphome/components/status_led/light/status_led_light.cpp @@ -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/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..e8018ed36f 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_ { 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..c550d60630 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,15 @@ void TemplateAlarmControlPanel::loop() { delay = this->arming_night_time_; } if ((millis() - this->last_update_) > delay) { +#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 this->publish_state(this->desired_state_); } return; @@ -137,6 +155,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)) { @@ -255,6 +278,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..0e3a5c77cf 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 { @@ -121,7 +122,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_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/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/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/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/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..6bfc49c675 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -2,8 +2,8 @@ #include -#include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esp_tls_crypto.h" 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..2211fc77d5 100644 --- a/esphome/components/weikai/weikai.cpp +++ b/esphome/components/weikai/weikai.cpp @@ -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..cffffca8c0 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2025.5.2" +__version__ = "2025.6.0b1" 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..75a7052c63 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(); }); @@ -106,7 +125,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->delay_with_select_(delay_time); } this->last_loop_ = last_op_end_time; @@ -139,15 +159,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 +182,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->delay_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 +239,117 @@ 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 (HighFrequencyLoopRequester::is_high_frequency()) + return true; // fd sets via select are not updated in high frequency looping - so force true fallback behavior + if (fd < 0 || fd >= FD_SETSIZE) + return false; + + return FD_ISSET(fd, &this->read_fds_); +} +#endif + +void Application::delay_with_select_(uint32_t delay_ms) { +#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); + } + } 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..d95f45e757 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,6 +79,12 @@ 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, @@ -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"); @@ -247,6 +324,14 @@ class Application { void run_safe_shutdown_hooks(); + 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); + uint32_t get_app_state() const { return this->app_state_; } #ifdef USE_BINARY_SENSOR @@ -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 delay_with_select_(uint32_t delay_ms); + std::vector components_{}; std::vector looping_components_{}; @@ -555,6 +656,15 @@ class Application { uint32_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.h b/esphome/core/component.h index 7b3e12eb59..ce9f0289d0 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -110,6 +110,19 @@ class Component { virtual void on_shutdown() {} virtual void on_safe_shutdown() {} + /** 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() {} + uint32_t get_component_state() const; /** Mark this component as failed. Any future timeouts/intervals/setup/loop will no longer be called. 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/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..7d25e7d261 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> 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/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..d4bd0b7543 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.1 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..d634be98c4 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,6 +832,22 @@ def build_enum_type(desc) -> tuple[str, str]: return out, cpp +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) -> tuple[str, str]: public_content: list[str] = [] protected_content: list[str] = [] @@ -773,6 +859,28 @@ def build_message_type(desc: descriptor.DescriptorProto) -> tuple[str, str]: dump: list[str] = [] size_calc: list[str] = [] + # 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) @@ -941,24 +1049,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 +1079,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 @@ -1083,6 +1185,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 +1280,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..4c798c6b72 --- /dev/null +++ b/tests/integration/conftest.py @@ -0,0 +1,531 @@ +"""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, 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}") + + 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.""" + while line := await stream.readline(): + decoded_line = line.decode("utf-8", errors="replace") + lines.append(decoded_line.rstrip()) + # Echo to stdout/stderr in real-time + print(decoded_line.rstrip(), 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/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_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/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_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/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 +